使用Metis和Solana Kit自动化Pump.fun交易

  • QuickNode
  • 发布于 2026-05-11 13:15
  • 阅读 48

本文介绍了如何使用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 上的示例代码。

你将完成的任务

  • 为 Pump.fun API 请求创建自定义 RPC 传输层
  • 实现报价获取与 Swap 执行
  • 使用 Solana Kit 处理交易签名与发送
  • 通过示例脚本测试实现

你需要准备

  • 一个已启用 Metis Jupiter Swap API 插件的 Quicknode 账户(你也可以使用我们的公共端点 JupiterAPI.com,但公共端点仅支持部分方法,且可能有更严格的速率限制和交易费用)。
  • Node.js(推荐 21.0 或更高版本)
  • 熟悉 TypeScript 和 Solana 开发基础知识
  • 建议具备 Solana Kit 使用经验

Metis 概述

Metis 是一个强大的工具,供开发者访问 Solana 上的流动性。通过集成 Jupiter 的 V6 Swap API、Jupiter 的限价单 API、Pump.fun 交易、交易 WebSocket 等功能,你可获得构建强大交易工具所需的一切,从而访问 Solana 上的众多 DeFi 协议。

开始

1. 设置项目

首先,创建项目目录:

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",
}

2. 创建类型定义

如果你之前使用过 Solana Kit,就会知道在项目前端进行一些类型定义可以节省大量时间与精力。我们已为你创建了 Pump.fun API 的类型定义。你可以在此处找到完整的类型定义。

将文件的所有内容复制并粘贴到你的 types.ts 文件中。这些类型定义了 Pump.fun API 的请求与响应结构,你可在 Metis 文档中找到相关说明。

3. 设置环境变量

本指南需要你拥有一个启用了 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 地址:

Solana 端点

Metis 地址

JupiterAPI.com 公共端点

JupiterAPI.com 为 Metis API 提供了一个公共端点。公共端点仅支持 Pump.fun 的 /pump-fun/swap 方法,并且使用时会包含 Swap 费用。请查看 JupiterAPI.com 了解最新的费用与速率限制。

实现

现在让我们创建实现与测试脚本,以便与 Pump.fun API 交互。打开 index.ts,逐步实现每个部分。

1. 引入依赖并定义类型

首先,引入所需依赖,并为 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 端点的映射,以便在传输层中正确处理每个方法。

2. 实现 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 实例。

3. 实现请求处理函数

现在,让我们实现核心的请求处理功能。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 响应。

4. 实现交易处理

让我们添加交易签名和发送功能:

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 函数处理完整的交易生命周期:

  1. 从提供的私钥创建密钥对
  2. 解码 base64 编码的交易
  3. 签名交易
  4. 获取最新的区块哈希
  5. 为交易添加生命周期约束以确保其“新鲜”
  6. 使用 Solana Kit 的 sendAndConfirmTransaction 工厂发送并确认交易
  7. 返回交易签名以便追踪

5. 环境变量验证

最后,让我们编写一个简单的环境变量验证函数,确保所有必需的变量都已设置:

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
  }
}

干得好!

执行 Swap

如果你准备开始交易,可以通过向 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 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。