本文介绍了如何使用Solana Kit(原Solana Web3.js 2.0)与Metis Pump.fun API进行Pump.fun代币的报价查询和交换执行。内容涵盖项目搭建、类型定义、环境配置、自定义RPC传输实现、请求处理、交易签名与发送等步骤,提供完整的TypeScript代码示例。读者可借此构建自动化交易工具,实现Pump.fun上的公平启动代币交易。
Pump.fun 是一个基于 Solana 的无许可代币创建与交易平台,采用公平启动理念(例如,无预售或团队分配)。在本指南中,你将学习如何使用 Solana Kit 与 Metis Pump.fun API 交互,以编程方式获取报价并执行 Swap。我们将创建一个 TypeScript 实现,用于处理 API 请求、交易签名以及向 Solana 网络发送交易。
使用 Solana Web3.js Legacy (v1.x) 构建
本指南将引导你使用 Solana Kit 与 Metis Pump.fun API 交互。
如果你更倾向于使用 Solana Web3.js Legacy (v1.x) 构建,请查看我们在 GitHub 上的示例代码。
Metis 是一个强大的工具,供开发者访问 Solana 上的流动性。通过集成 Jupiter 的 V6 Swap API、Jupiter 的限价单 API、Pump.fun 交易、交易 WebSocket 等功能,你可获得构建强大交易工具所需的一切,从而访问 Solana 上的众多 DeFi 协议。
首先,创建项目目录:
mkdir pumpfun-integration && cd pumpfun-integration
初始化 Node.js 项目:
npm init -y
安装所需依赖:
npm install @solana/kit dotenv
如果你尚未全局安装 TypeScript,可将其作为开发依赖添加到项目中:
npm install --save-dev typescript ts-node @types/node
初始化 TypeScript 配置(tsconfig.json):
tsc --init
创建本项目将使用的文件:
echo > index.ts && echo > types.ts && echo > .env
更新 package.json 中的脚本,以便运行 TypeScript 文件:
"scripts": {
"start": "ts-node index.ts",
}
如果你之前使用过 Solana Kit,就会知道在项目前端进行一些类型定义可以节省大量时间与精力。我们已为你创建了 Pump.fun API 的类型定义。你可以在此处找到完整的类型定义。
将文件的所有内容复制并粘贴到你的 types.ts 文件中。这些类型定义了 Pump.fun API 的请求与响应结构,你可在 Metis 文档中找到相关说明。
本指南需要你拥有一个启用了 Metis API 的 Quicknode 账户以及相应的 Solana 端点。你可以在此注册 Quicknode 账户。
打开你的 .env 文件,添加你的钱包私钥(如果尚未拥有,可运行 solana-keygen new 生成)、Metis 端点以及 Solana 主网 HTTP 和 WSS URL:
WALLET_SECRET_KEY=[1, 1, 1, 1, 1, 1 ...etc.]
METIS_URL='https://jupiter-swap-api.quiknode.pro/YOUR_ENDPOINT'
HTTP_ENDPOINT='https://example.solana-mainnet.quiknode.pro/123456/'
WSS_ENDPOINT='wss://example.solana-mainnet.quiknode.pro/123456/'
请务必将你的 HTTP 和 WSS 端点替换为 Quicknode 仪表板中提供的 Solana 端点,并将 https://jupiter-swap-api.quiknode.pro/YOUR_ENDPOINT 替换为你自己的 Metis 端点。你可以在 Quicknode 仪表板的插件页面(https://dashboard.quicknode.com/endpoints/YOUR_ENDPOINT/add-ons)找到你的 Metis 地址:


JupiterAPI.com 公共端点
JupiterAPI.com 为 Metis API 提供了一个公共端点。公共端点仅支持 Pump.fun 的 /pump-fun/swap 方法,并且使用时会包含 Swap 费用。请查看 JupiterAPI.com 了解最新的费用与速率限制。
现在让我们创建实现与测试脚本,以便与 Pump.fun API 交互。打开 index.ts,逐步实现每个部分。
首先,引入所需依赖,并为 Metis PumpFun API 定义类型:
import {
Rpc,
createRpc,
RpcTransport,
createJsonRpcApi,
address,
getBase64Encoder,
FullySignedTransaction,
TransactionMessageBytes,
getTransactionDecoder,
signTransaction,
createKeyPairFromBytes,
TransactionWithBlockhashLifetime,
getSignatureFromTransaction,
createSolanaRpcSubscriptions,
sendAndConfirmTransactionFactory,
createSolanaRpc,
SolanaRpcApi,
RpcSubscriptions,
SolanaRpcSubscriptionsApi,
getAddressDecoder,
getAddressFromPublicKey
} from "@solana/kit";
import {
HttpRequestMethod,
PumpFunEndpoint,
PumpFunQuoteParams,
PumpFunQuoteResponse,
PumpFunRequest,
PumpFunSwapInstructionsResponse,
PumpFunSwapParams,
PumpFunSwapResponse,
SignAndSendTransactionParams
} from "./types";
import dotenv from 'dotenv';
dotenv.config();
type MetisPumpFunApi = {
pumpfun_quote(params: PumpFunQuoteParams): Promise<PumpFunQuoteResponse>;
pumpfun_swap(params: PumpFunSwapParams): Promise<PumpFunSwapResponse>;
pumpfun_swap_instructions(params: PumpFunSwapParams): Promise<PumpFunSwapInstructionsResponse>;
}
const METHOD_TO_ENDPOINT: Record<string, PumpFunEndpoint> = {
pumpfun_quote: {
path: 'pump-fun/quote',
method: 'GET'
},
pumpfun_swap: {
path: 'pump-fun/swap',
method: 'POST'
},
pumpfun_swap_instructions: {
path: 'pump-fun/swap-instructions',
method: 'POST'
}
};
const PUBLIC_ENDPOINT_URL = 'https://public.jupiterapi.com';
除了我们在上一步中定义的类型外,我们还引入了必要的 Solana Kit 依赖,并定义了 MetisPumpFunApi 类型,它代表我们将实现的 Pump.fun API 方法。这是 Metis API 的一个子集,本指南将使用它。如果你对此不太熟悉,建议查看我们的指南 使用 Solana Kit 构建自定义 API。我们还创建了一个方法名称到 API 端点的映射,以便在传输层中正确处理每个方法。
接下来,添加一些辅助函数来配置 PumpFun API 并处理请求。将以下代码添加到你的 index.ts:
function createPumpFunUrl(metisEndpoint: string, method: string): URL {
const baseUrl = metisEndpoint.replace(/\/$/, ''); // 移除末尾的斜杠(如果存在)
const endpointPath = METHOD_TO_ENDPOINT[method].path;
return new URL(`${baseUrl}/${endpointPath}`);
}
function createPumpFunTransport(metisEndpoint: string): RpcTransport {
return async <TResponse>(...args: Parameters<RpcTransport>): Promise<TResponse> => {
const { method, params } = args[0].payload as { method: string; params: PumpFunRequest };
const url = createPumpFunUrl(metisEndpoint, method);
const normalizedParams = Array.isArray(params) ? params[0] : params;
switch (METHOD_TO_ENDPOINT[method].method) {
case 'GET':
return handlePumpFunGET<PumpFunRequest, TResponse>(url, normalizedParams);
case 'POST':
return handlePumpFunPOST<PumpFunRequest, TResponse>(url, normalizedParams);
default:
throw new Error(`未知的 HTTP 方法,对应 PumpFun 方法: ${method}`);
}
};
}
function createPumpFunApi(metisEndpoint: string): Rpc<MetisPumpFunApi> {
const api = createJsonRpcApi<MetisPumpFunApi>();
const transport = createPumpFunTransport(metisEndpoint);
return createRpc({ api, transport });
}
以下是每个函数的作用说明:
createPumpFunUrl:基于 Metis 端点和方法名称创建 Pump.fun API 端点的 URL 对象(并移除末尾斜杠)。createPumpFunTransport:创建自定义 RPC 传输函数,用于验证端点/方法、规范化参数,并根据 HTTP 方法(GET/POST)将请求路由到适当的处理函数——我们接下来将定义这些处理函数。createPumpFunApi:使用自定义传输函数创建 PumpFun API 的 RPC 实例。现在,让我们实现核心的请求处理功能。GET 方法将使用 URL 搜索参数传递请求参数,而 POST 方法则在请求体中发送参数。将以下代码添加到你的 index.ts:
async function handlePumpFunGET<TParams, TResponse>(
url: URL,
params: TParams
): Promise<TResponse> {
if (typeof params === 'object' && params !== null) {
Object.entries(params as Record<string, unknown>).forEach(([key, value]) => {
url.searchParams.append(key, String(value));
});
}
const response = await fetch(url.toString(), {
method: 'GET',
redirect: 'follow',
headers: {
'Content-Type': 'application/json',
}
});
if (!response.ok) {
throw new Error(`向 ${url} 发送 GET 请求时出错: ${response.statusText}`);
}
return await response.json() as TResponse;
}
async function handlePumpFunPOST<TParams, TResponse>(
url: URL,
params: TParams
): Promise<TResponse> {
try {
const response = await fetch(url.toString(), {
method: 'POST',
redirect: 'follow',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(params),
});
if (!response.ok) {
throw new Error(`向 ${url} 发送 POST 请求时出错: ${response.statusText}`);
}
return await response.json() as TResponse;
} catch (error) {
console.error('发送 POST 请求时出错:', error);
throw error;
}
}
每个函数的作用如下:
handlePumpFunGET:将请求参数追加到 URL 搜索参数,并向 Pump.fun API 端点发送 GET 请求。返回 JSON 响应。handlePumpFunPOST:向 Pump.fun API 端点发送 POST 请求,请求参数包含在请求体中。返回 JSON 响应。让我们添加交易签名和发送功能:
async function signAndSendTransaction({
transactionBase64,
signerSecretKey,
solanaRpc,
solanaRpcSubscriptions,
commitment = 'confirmed'
}: SignAndSendTransactionParams): Promise<string> {
// 从私钥创建签名者密钥对
const signerKeypair = await createKeyPairFromBytes(
new Uint8Array(signerSecretKey)
);
// 解码 base64 格式的交易
const transactionBytes = getBase64Encoder().encode(transactionBase64) as TransactionMessageBytes;
const transactionDecoder = getTransactionDecoder();
const decodedTransaction = transactionDecoder.decode(transactionBytes);
// 签名交易
const signedTransaction = await signTransaction(
[signerKeypair],
decodedTransaction
);
// 获取最新区块哈希并准备包含生命周期的交易
const { value: { lastValidBlockHeight, blockhash } } = await solanaRpc.getLatestBlockhash().send();
const signedTransactionWithLifetime: FullySignedTransaction & TransactionWithBlockhashLifetime = {
...signedTransaction,
lifetimeConstraint: {
blockhash,
lastValidBlockHeight,
},
};
// 获取交易签名
const transactionSignature = getSignatureFromTransaction(signedTransactionWithLifetime);
// 创建 sendAndConfirm 函数
const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({
rpc: solanaRpc,
rpcSubscriptions: solanaRpcSubscriptions,
});
// 发送并确认交易
await sendAndConfirmTransaction(signedTransactionWithLifetime, {
commitment,
});
return transactionSignature;
}
这个函数将让执行我们 base64 编码的交易变得轻而易举。signAndSendTransaction 函数处理完整的交易生命周期:
sendAndConfirmTransaction 工厂发送并确认交易最后,让我们编写一个简单的环境变量验证函数,确保所有必需的变量都已设置:
function validateEnv() {
const envVars = ['WALLET_SECRET_KEY','HTTP_ENDPOINT','WSS_ENDPOINT']; // 'METIS_URL' 验证非必需,因为我们有公共端点作为回退
envVars.forEach((envVar) => {
if (!process.env[envVar]) {
throw new Error(`环境变量 ${envVar} 是必需的`);
}
});
}
我们只是检查每个预期的环境变量,如果未设置则抛出错误。提醒:切勿在客户端代码中暴露你的 Quicknode API 密钥。始终将其安全地保存在后端。请查看我们的 指南:如何保护你的端点 - 前端最佳实践,了解更多端点安全信息。
让我们创建一个新的函数 main 来测试我们的 API。注意:如果你使用的是公共端点,此方法将无法正常工作。 将以下代码添加到你的 index.ts:
async function main() {
validateEnv();
const metisUrl = process.env.METIS_URL ?? PUBLIC_ENDPOINT_URL;
const rpcUrl = process.env.HTTP_ENDPOINT as string;
const rpcSubscriptionsUrl = process.env.WSS_ENDPOINT as string;
const signerSecretKey = JSON.parse(process.env.WALLET_SECRET_KEY as string) as number[];
const signerKeypair = await createKeyPairFromBytes(new Uint8Array(signerSecretKey));
const wallet = await getAddressFromPublicKey(signerKeypair.publicKey);
const targetMint = address("8gXN67Nmw9FZQjunJZzRoi2Qf1ykZtN9Q3BqxhCypump");
const pumpFunApi = createPumpFunApi(metisUrl);
const solanaRpc: Rpc<SolanaRpcApi> = createSolanaRpc(rpcUrl);
const solanaRpcSubscriptions: RpcSubscriptions<SolanaRpcSubscriptionsApi>
= createSolanaRpcSubscriptions(rpcSubscriptionsUrl);
try {
const pumpFunQuote = await pumpFunApi.pumpfun_quote({
type: 'BUY',
mint: targetMint,
amount: 1_000_000,
}).send();
console.log(`PumpFun 报价:\n ${JSON.stringify(pumpFunQuote.quote, null, 2)}`);
} catch (error) {
console.error('获取 PumpFun 报价时出错:', error);
}
}
main().catch(console.error);
在这里,我们声明环境变量,创建 PumpFun API 实例,并从 PumpFun API 获取报价。我们使用 pumpfun_quote 方法获取购买指定代币 0.001 SOL 的报价。你可以根据需要调整参数。
在终端中运行以下命令启动脚本:
npm start
如果一切设置正确,你应该会看到报价响应记录到控制台,如下所示:
PumpFun 报价:
{
"mint": "8gXN67Nmw9FZQjunJZzRoi2Qf1ykZtN9Q3BqxhCypump",
"bondingCurve": "6TWadAgufwkQqZfKm1fqLgSQBUGKoMup7x5uC8fHUtnZ",
"type": "BUY",
"inAmount": "1000000",
"inAmountUi": 0.001,
"inTokenAddress": "So11111111111111111111111111111111111111112",
"outAmount": "33339932484",
"outAmountUi": 33339.932484,
"outTokenAddress": "8gXN67Nmw9FZQjunJZzRoi2Qf1ykZtN9Q3BqxhCypump",
"meta": {
"isCompleted": false,
"outDecimals": 6,
"inDecimals": 9,
"totalSupply": "1000000000000000",
"currentMarketCapInSol": 29.993096666
}
}
干得好!
如果你准备开始交易,可以通过向 main 函数添加以下代码来构建交易并将其发送到网络:
try {
const pumpFunQuote = await pumpFunApi.pumpfun_swap({
wallet,
type: 'BUY',
mint: targetMint,
inAmount: 1_000_000,
priorityFeeLevel: 'high', // 可选:设置优先费用等级
}).send();
const sig = await signAndSendTransaction({
transactionBase64: pumpFunQuote.tx,
signerSecretKey: JSON.parse(process.env.WALLET_SECRET_KEY as string) as number[],
solanaRpc,
solanaRpcSubscriptions,
});
console.log(`交易签名: ${sig}`);
} catch (error) {
console.error('获取 PumpFun 报价时出错:', error);
}
警告: 此代码将在 Solana 网络上执行真实交易,并导致不可逆的资金转移。在运行之前,请确保你已充分理解其影响。
你现在拥有了一个使用 Solana Kit 与 Metis Pump.fun API 交互的健壮实现。该实现提供了类型安全、抗错误的报价获取与 Pump.fun 平台 Swap 执行方式。如果你想继续构建,请查看我们的 指南:如何在 Solana 上构建 Jupiter 交易机器人,获取更多将该项目提升到新高度的灵感。
请始终在开发环境中充分测试,然后再部署到生产环境,并确保采取适当的安全措施来处理私钥和 Quicknode 凭据。
要了解更多关于 Solana 开发的信息,请查看我们在 Quicknode 指南 上的其他指南。如需支持或提出问题,请加入我们的 Discord 社区 或通过 Twitter 联系我们。
如果你有任何反馈或对新主题的建议,请告诉我们。我们期待你的意见。
- 原文链接: quicknode.com/guides/sol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!
作者暂未设置收款二维码