本指南仅供学习目的
本指南仅供教育目的,不应被视为财务建议。交易机器人可能存在风险,可能导致经济损失。请务必自行研究,并考虑在使用交易机器人或参与交易活动之前咨询财务顾问。确保在使用真实资金之前,你运行的所有代码都是安全且经过彻底测试的。
概述
在本指南中,我们将学习如何创建一个 Solana 交易机器人,该机器人使用QuickNode 的 Metis 插件 和Yellowstone gRPC 来复制指定钱包在 Pump.fun DEX 上的交易。此指南面向具备 JavaScript、Solana 和基本 DeFi 概念知识的开发者。
你将要做的事情
- 了解 Pump.fun 和 Yellowstone 的概述
- 创建一个 JavaScript 交易机器人,它监控 Pump.fun 上钱包的交易,并根据预定义策略复制其购买交易
- 模拟目标钱包交易并测试机器人的功能
更喜欢使用 Solana Web3.js 2.0 或 TypeScript?
如果你更喜欢使用 TypeScript 或新的 Solana Web3.js 2.0 库,请查看我们关于使用 Yellowstone 监控程序和如何使用 Solana Web3.js 2.0 构建 Pump.fun API 的指南。
你将需要的东西
- 对 Solana 开发 和 DeFi 概念 的中级知识
- 具备 JavaScript 和 Node.js 的经验
- 一个具有 SOL 余额的 Solana 文件系统钱包(运行
solana-keygen -h获取支持以创建新钱包的帮助) - 已启用 Yellowstone gRPC 插件的 QuickNode 账户
Solana 主要网 RPC 端点
使用你的 QuickNode 端点连接到 Solana 集群
要在 Solana 上进行构建,你需要一个 API 端点以连接到网络。你可以使用公共节点或自行部署和管理基础架构;但是,如果你希望享受 8 倍的更快响应时间,可以将繁重的工作交给我们。
了解为什么超过 50% 的 Solana 项目选择 QuickNode,并在这里 注册免费账户。我们将使用一个 SolanaMainnet 端点。
复制 HTTP 提供者链接:
什么是 Metis?
Metis 是一个强大的工具,帮助开发者访问 Solana 上的流动性。通过集成 Jupiter 的 V6 交换 API、限制订单 API、Pump.fun 交易、交易 WebSocket 等,你可以访问构建强大交易工具所需的工具,以访问 Solana 上的多个 DeFi 协议。Metis 可作为 QuickNode 市场上的附加组件, 在这里 或者可以通过 JupiterAPI.com 访问公共端点。
使用 Pump.fun API
在本指南中,我们将重点关注 /pump-fun/swap 端点(文档),该端点允许我们获取用于在 Pump.fun 上执行交换的序列化交易。此端点需要以下参数:
wallet:执行交易的钱包的公钥type:交易类型("BUY" 或 "SELL")mint:被交易的代币的 mint 地址inAmount:输入代币的数量(以原始单位表示)priorityFeeLevel(可选):交易的优先费用级别("low"、"medium"、"high" 或 "auto")slippageBps(可选):允许的最大滑点(以基点表示)
来自该端点的响应包含一个 base64 编码的 Solana 交易,可以签名并发送到网络以执行交易。
什么是 Yellowstone?
Yellowstone 是一个市场附加组件,提供基于 gRPC 的 API,使开发者能够创建自定义订阅并在 Solana 网络上实时接收事件更新。这使其成为构建需要实时监控区块链活动的应用程序(例如交易机器人、分析平台和去中心化应用程序 dApps)的优秀工具。
要使用 Yellowstone,我们需要创建一个订阅请求,指定我们希望监控的账户、交易和其他事件。Yellowstone 将流式传输我们指定事件的实时更新。
有关 Yellowstone 的更多信息,请查看:
- 文档
- 入门指南
设置项目
在开始构建交易机器人之前,让我们设置项目并安装必要的依赖项。
为你的项目创建一个新目录,并在终端中导航到该目录。
通过运行以下命令初始化一个新的 Node.js 项目:
npm init -y- 通过运行以下命令安装所需的依赖项:
npm install @solana/web3.js@1 bs58 dotenv @triton-one/yellowstone-grpc@solana/web3.js:与 Solana 区块链交互的 Solana Web3.js 库的旧版本bs58:用于处理 Base58 编码/解码的库dotenv:用于从.env文件加载环境变量的库@triton-one/yellowstone-grpc:Yellowstone gRPC 客户端库
在你的项目目录中创建一个名为
bot.js的新文件。在你的项目目录中创建一个
.env文件,并添加以下环境变量:
SOLANA_RPC=<your_solana_rpc_endpoint> # https://example.quiknode.pro/replace-me-123/ SECRET_KEY=<your_wallet_secret_key> # [0, 0, ..., 0] METIS_ENDPOINT=<your_metis_endpoint> # https://jupiter-swap-api.quiknode.pro/REPLACE_ME YELLOWSTONE_ENDPOINT=<your_yellowstone_endpoint> # https://example.solana-mainnet.quiknode.pro:10000 YELLOWSTONE_TOKEN=<your_yellowstone_token> # abc...xyz替换占位符为你的实际值:
SOLANA_RPC:你的 QuickNode Solana 主要网 RPC 端点(你可以在 QuickNode 仪表板 中找到此内容)SECRET_KEY:你的 Solana 钱包的秘密密钥(JSON 数组格式,例如[0, 0, ..., 0])。确保此钱包充满 SOL 以便机器人能执行交易。METIS_ENDPOINT:你的 QuickNode Metis 端点用于 Pump.fun API(例如https://jupiter-swap-api.quiknode.pro/...)。如果没有 Metis 附加组件,你可以使用公共端点:https://public.jupiterapi.com(注意:公共端点可能会产生交易费用——请查看 jupiterapi.com 获取详细信息)。YELLOWSTONE_ENDPOINT:你的 Yellowstone 端点(注意:这应是以 gRPC 端点,结尾需为:10000,有关更多信息,请查看 这里)YELLOWSTONE_TOKEN:你的 Yellowstone API Token(在 这里 查找你的Token)
构建交易机器人
现在我们已经设置好了项目,让我们开始构建交易机器人。🤖
从高层来看,我们将要做的事情如下:
- 创建一个交易机器人类,初始化必要的配置和依赖项。
- 创建一个监控功能,使用 Yellowstone 监听目标钱包在 Pump.fun 上的购买交易。
- 实现一个方法,这个方法响应来自目标交易的特定交易,利用 Pump.fun API 获取复制的交换交易并在 Solana 上执行交易。
- 记录成功的交易日志文件以进行跟踪和分析。
配置机器人
打开 bot.js 文件并添加以下代码:
require("dotenv").config(); const fs = require("fs"); const fetch = require("node-fetch"); const bs58 = require("bs58").default; const { Connection, Keypair, VersionedTransaction, LAMPORTS_PER_SOL, PublicKey, } = require("@solana/web3.js"); const Client = require("@triton-one/yellowstone-grpc").default; const { CommitmentLevel } = require("@triton-one/yellowstone-grpc"); class CopyTradeBot { config = { WATCH_LIST: [\ "WALLET_TO_TRACK_1",\ "WALLET_TO_TRACK_2",\ "WALLET_TO_TRACK_3",\ //...\ ], PUMP_FUN: { PROGRAM_ID: "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", FEE_ACCOUNT: "CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM", BUY_DISCRIMINATOR: Buffer.from([102, 6, 61, 18, 1, 218, 235, 234]), SELL_DISCRIMINATOR: Buffer.from([51, 230, 133, 164, 1, 127, 131, 173]), TOKEN_DECIMALS: 6, TARGET_ACCOUNTS: { BUY: [\ { name: "mint", index: 2 },\ { name: "user", index: 6 },\ ], SELL: [\ { name: "mint", index: 2 },\ { name: "user", index: 6 },\ ], }, }, MIN_TX_AMOUNT: LAMPORTS_PER_SOL / 1000, BUY_AMOUNT: LAMPORTS_PER_SOL / 1000, LOG_FILE: "pump_fun_swaps.json", COMMITMENT: CommitmentLevel.CONFIRMED, TEST_MODE: true }; constructor() { this.validateEnv(); this.connection = new Connection(process.env.SOLANA_RPC); this.wallet = Keypair.fromSecretKey( Uint8Array.from(JSON.parse(process.env.SECRET_KEY)) ); console.log("🤖 机器人钱包:", this.wallet.publicKey.toBase58()); console.log("监控地址:"); this.config.WATCH_LIST.forEach((address) => console.log(" -", address)); } // ... (其他方法将在这里添加) } 这段代码设置了交易机器人的初始配置。配置每个部分的作用如下:
WATCH_LIST:一个监控交易的Wallet地址数组。为了本示例,请从你的浏览器钱包中使用一个钱包地址(例如 Phantom、SolFlare、Backpack 等)。我们将在本指南的后面使用该钱包在 Pump.fun 上执行交易。PUMP_FUN:一个包含 Pump.fun 程序配置详细信息的对象,包括程序 ID、费用账户、购买和出售指令的识别符、代币小数位数以及指令中目标账户(mint 用户)索引的配置。这些都是从 Pump.fun 的程序 IDL 和指令中得到的已知常量。在我们的 Solana 程序 IDL 指南 中获取更多信息。MIN_TX_AMOUNT:机器人考虑交易所需的最低 SOL 数量(如果目标钱包消费的金额小于该金额,我们将直接忽略该交易)。BUY_AMOUNT:机器人用于执行购买交易的 SOL 数量。为了演示,我们将只设置一个静态购买金额——如果我们看到一个想要复制的交易,我们将固定金额的 SOL 进行复制交易。完成指南后,你可以根据自己的用例自行尝试不同策略。LOG_FILE:机器人将成功交易记录的文件路径。COMMITMENT:Yellowstone 订阅的承诺级别。TEST_MODE:启用/禁用测试模式的标志。当设置为true时,机器人将仿真交易,而不是在 Solana 网络上实际执行。这对于测试机器人的功能而不冒用真实资金是有用的。
constructor 方法验证所需的环境变量,创建了一个 Solana 连接,并使用提供的秘密密钥初始化机器人的钱包。
让我们添加我们的其他方法!
验证环境变量
我们在上面的构造函数中已经添加了一个 validateEnv() 方法,用于确保所需的环境变量已设置。该方法在 CopyTradeBot 类实例化时被触发。如果缺少任何必需的变量,机器人将抛出错误并退出。
在你的 CopyTradeBot 类中添加以下方法以验证所需的环境变量:
validateEnv = () => { const requiredEnvs = [\ "SOLANA_RPC",\ "SECRET_KEY",\ "METIS_ENDPOINT",\ "YELLOWSTONE_ENDPOINT",\ "YELLOWSTONE_TOKEN",\ ]; requiredEnvs.forEach((env) => { if (!process.env[env]) { throw new Error(`缺少必需的环境变量: ${env}`); } }); }; 这将检查所有必需的环境变量在继续之前是否已设置。
获取交换交易
让我们创建一个 fetchSwapTransaction() 方法,该方法将负责与 Pump.fun API(通过你的 Metis 端点)通信,以获取执行交换所需的序列化交易。你提供的信息包括钱包地址、交换类型(例如 "BUY")、代币 mint 和交换金额。
fetchSwapTransaction = async ({ wallet, type, mint, inAmount, priorityFeeLevel = "high", slippageBps = "100", }) => { const body = JSON.stringify({ wallet, type, mint, inAmount, priorityFeeLevel, slippageBps, }); const res = await fetch(`${process.env.METIS_ENDPOINT}/pump-fun/swap`, { method: "POST", headers: { "Content-Type": "application/json" }, body, }); if (!res.ok) { throw new Error(`交换指令获取错误: ${await res.text()}`); } return res.json(); }; 该方法的两个主要组件如下:
body:包含 Pump.fun API 生成交换交易所需的参数。fetch:向 Pump.fun 的/swap端点发送 POST 请求,并提供参数。如果成功,你将获得一个 JSON 响应,其中包含一个 base64 编码的交易。
有关此方法的更多信息,请查看我们 Pump.fun API 文档 在这里。
签署交易
一旦 Pump.fun API 返回一个 base64 编码的交易,你需要使用机器人的 Keypair 在本地进行签名。让我们在 CopyTradeBot 类中添加一个负责此操作的 signTransaction() 方法:
signTransaction = async (swapTransaction) => { const transaction = VersionedTransaction.deserialize( Buffer.from(swapTransaction, "base64") ); const latestBlockHash = await this.connection.getLatestBlockhash(); transaction.message.recentBlockhash = latestBlockHash.blockhash; transaction.sign([this.wallet]); const txBuffer = Buffer.from(transaction.serialize()); const txBase64 = txBuffer.toString("base64"); return txBase64; }; 因为交易已经序列化,所以我们需要将其反序列化为一个 VersionedTransaction 对象,用机器人的钱包进行签名,然后重新序列化为原始字节以供广播:
- 反序列化:将 base64 编码的交易转换为一个
VersionedTransaction对象。 - 获取最新区块哈希:从 Solana 网络中获取最新的区块哈希并将其应用于交易。
- 签名:将机器人的钱包签名添加到交易中。
- 序列化:返回完全签名的交易的 base64 编码字符串,准备广播。
发送和确认交易
下一步是将签名后的交易广播到 Solana 网络。在你的机器人类中添加以下 sendAndConfirmTransaction() 方法:
sendAndConfirmTransaction = async (signedTxBase64) => { try { const txid = await this.connection.sendEncodedTransaction(signedTxBase64, { skipPreflight: false, encoding: 'base64' }); const timeout = 30 * 1000; const pollInterval = 3 * 1000; const start = Date.now(); while (Date.now() - start < timeout) { const response = await this.connection.getSignatureStatuses([txid]); if (!response) { await new Promise(resolve => setTimeout(resolve, pollInterval)); continue; } const statuses = response.value; if (!statuses || statuses.length === 0) { await new Promise(resolve => setTimeout(resolve, pollInterval)); continue; } const status = statuses[0]; if (status === null) { await new Promise(resolve => setTimeout(resolve, pollInterval)); continue; } if (status.err) { throw new Error(`交易失败: ${JSON.stringify(status.err)}`); } if (status.confirmationStatus && (status.confirmationStatus === 'confirmed' || status.confirmationStatus === 'finalized')) { return txid; } await new Promise(resolve => setTimeout(resolve, pollInterval)); } throw new Error(`交易确认超时,超时后 ${timeout}ms`); } catch (error) { throw {error, base64: Buffer.from(rawTransaction).toString("base64")}; } }; 该方法将签名后的交易发送到 Solana 网络,并等待其确认。使用以下步骤:
sendEncodedTransaction():将签名交易作为 base64 字符串发送到集群,并返回交易签名(txid)——请注意,由于我们在上一步中序列化了交易,因此可以使用sendEncodedTransaction()方法。创建该交易的 API 已经模拟了交易以计算计算单元,因此可以跳过预检查。- 最后,我们创建一个简单的轮询函数,使用
getSignatureStatuses()检查交易状态,直到确认或超时。有关最佳实践的更多信息,请查看我们的文档 在这里。
记录交换
为了记录和调试,logSwap() 方法将每个交换的 JSON 记录写入日志文件:
logSwap = (swapLog) => { const logs = fs.existsSync(this.config.LOG_FILE) ? JSON.parse(fs.readFileSync(this.config.LOG_FILE, "utf-8")) : []; logs.push(swapLog); fs.writeFileSync(this.config.LOG_FILE, JSON.stringify(logs, null, 2)); }; 这里的工作原理如下:
- 现有日志:如果文件
pump_fun_swaps.json存在,将其读取并解析为数组。 - 附加新日志:将
swapLog对象添加到数组中。 - 回写:将更新后的数组写回磁盘。
处理鲸鱼购买
每当我们检测到来自 WATCH_LIST 中“鲸鱼”的购买交易超过某个阈值(MIN_TX_AMOUNT)时,我们需要执行某些逻辑。让我们在 CopyTradeBot 类中创建一个方法 handleWhaleBuy(),在知道“鲸鱼”完成达成我们标准的购买交易后执行复制交易。将以下方法添加到你的 CopyTradeBot 类中:
handleWhaleBuy = async (whalePubkey, tokenMint, lamportsSpent, copiedTxid) => { if (lamportsSpent < this.config.MIN_TX_AMOUNT) return; try { const inAmount = this.config.BUY_AMOUNT; const response = await this.fetchSwapTransaction({ wallet: this.wallet.publicKey.toBase58(), type: "BUY", mint: tokenMint, inAmount, slippageBps: "300", }); if (!response.tx) { throw new Error(`意外的响应格式: ${JSON.stringify(response)}`); } const { tx } = response; const signedTransaction = await this.signTransaction(tx); let txid = '模拟-TxID'; if (!this.config.TEST_MODE) { txid = await this.sendAndConfirmTransaction(signedTransaction); } console.log("🎯 - 复制 - TxID:", txid); this.logSwap({ event: "COPY_BUY", txid, copiedTxid, tokenMint, lamportsSpent, whalePubkey, timestamp: new Date().toISOString(), }); } catch (err) { this.logSwap({ event: "COPY_BUY_ERROR", error: typeof err === "string" ? err : err && typeof err.message === "string" ? err.message : JSON.stringify(err, null, 2) || "未知错误", copiedTxid, timestamp: new Date().toISOString(), }); } }; 让我们查看此方法的关键组件:
lamportsSpent:目标钱包在原始交易中花费的总 SOL。这必须超过MIN_TX_AMOUNT,我们才会去复制该交易。你当然可以在这里实现自己的逻辑。inAmount:我们机器人将花费多少 SOL 来复制交易。它设置为我们配置中的BUY_AMOUNT。fetchSwapTransaction():我们请求 Pump.fun 进行我们的购买交易的交易。signTransaction():使用我们的钱包对其进行签名。sendAndConfirmTransaction():将签名后的交易发送到 Solana 网络并等待确认。(如果启用TEST_MODE则跳过)logSwap():将复制交易尝试记录在日志文件中。
构建 Yellowstone 订阅
现在我们已经在执行方法中处理完了交易,那么我们需要设置一个 Yellowstone 订阅,以便监控目标钱包在 Pump.fun 上的购买交易。Yellowstone 将允许我们监听符合特定条件的交易,然后我们可以解析交易指令数据,以确保我们做出适当的响应。
创建订阅请求
我们需要告诉 Yellowstone 我们关心监测哪些地址或程序。让我们在 CopyTradeBot 类中添加几个方法:
createSubscribeRequest = () => { const { WATCH_LIST, PUMP_FUN, COMMITMENT } = this.config; return { accounts: {}, slots: {}, transactions: { pumpFun: { accountInclude: WATCH_LIST, accountExclude: [], accountRequired: [PUMP_FUN.FEE_ACCOUNT, PUMP_FUN.PROGRAM_ID], }, }, transactionsStatus: {}, entry: {}, blocks: {}, blocksMeta: {}, commitment: COMMITMENT, accountsDataSlice: [], ping: undefined, }; }; sendSubscribeRequest = (stream, request) => { return new Promise((resolve, reject) => { stream.write(request, (err) => { if (err) reject(err); else resolve(); }); }); }; handleStreamEvents = (stream) => { return new Promise((resolve, reject) => { stream.on("data", this.handleData); stream.on("error", (error) => { console.error("流错误:", error); reject(error); stream.end(); }); stream.on("end", () => { console.log("流结束"); resolve(); }); stream.on("close", () => { console.log("流关闭"); resolve(); }); }); }; 在这里我们定义了三个函数:
createSubscribeRequest():构建带有我们关心的账户/过滤器的订阅请求对象。accountsRequired将确保我们只看到涉及 Pump.fun 的费用账户和程序 ID 的交易——费用账户帮助我们标识 Pump.fun 交易与程序上的其他类型的交易。accountInclude过滤器确保我们只看到来自WATCH_LIST钱包的交易(你可以将它们视为“或者”连接,因此只要一笔交易包含这些账户中的任何一笔,就会被显示)。sendSubscribeRequest():将订阅发送到网络。handleStreamEvents():勾勒出我们将如何处理传入的数据、错误和流关闭。重要的是,在此,我们接收到新数据时调用this.handleData。换句话说,当 Yellowstone 返回符合我们过滤条件的交易时,我们将使用handleData()方法处理它。
处理传入交易
handleData() 方法是我们解析 Pump.fun 交易详细信息的地方。这对于我们的用例极为重要,因为这是我们将确定发生了哪种类型的交易(例如购买或出售)以及交易规模的地方。将以下方法添加到你的机器人类中,我们将对此进行详细讲解:
handleData = (data) => { if ( !this.isSubscribeUpdateTransaction(data) || !data.filters.includes("pumpFun") ) { return; } const transaction = data.transaction?.transaction; const message = transaction?.transaction?.message; const innerInstructions = transaction?.meta?.innerInstructions; const flattenedInnerInstructions = innerInstructions?.flatMap((ix) => ix.instructions || []) || []; const allInstructions = [\ ...message.instructions,\ ...flattenedInnerInstructions,\ ]; if (!transaction || !message || transaction?.meta?.err) return; const formattedSignature = this.convertSignature(transaction.signature); const matching = allInstructions.find(this.matchesInstructionDiscriminator); if (!matching) { console.log(`❓ - 未知 - TxID: ${formattedSignature.base58}`); return; } const { amount, solAmount } = this.getInstructionData(matching.data); if (solAmount < this.config.MIN_TX_AMOUNT) return; const txType = this.getTransactionType(matching.data); const icon = txType === "SELL" ? "📉" : txType === "BUY" ? "🎯" : "❓"; console.log(`${icon} - ${txType} - TxID: ${formattedSignature.base58}`); const accountKeys = message.accountKeys; const accountsToInclude = this.config.PUMP_FUN.TARGET_ACCOUNTS[txType]; const includedAccounts = accountsToInclude.reduce((acc, { name, index }) => { const accountIndex = matching.accounts[index]; const publicKey = accountKeys[accountIndex]; acc[name] = new PublicKey(publicKey).toBase58(); return acc; }, {}); if (includedAccounts.mint) { console.log(" Mint:", includedAccounts.mint); } if (includedAccounts.user) { console.log(" User:", includedAccounts.user); } console.log( " 代币数量:", amount / Math.pow(10, this.config.PUMP_FUN.TOKEN_DECIMALS) ); console.log(" SOL 数量:", solAmount / LAMPORTS_PER_SOL); if (txType === "BUY") { (async () => { try { await this.handleWhaleBuy( includedAccounts.user, includedAccounts.mint, solAmount, formattedSignature.base58 ); } catch (error) { console.error("处理(handleWhaleBuy)中的错误:", error); } })(); } }; 这里我们做的工作包括:
- 首先,我们确保传入的数据是来自 Yellowstone 的交易更新,且涉及 Pump.fun 程序。如果由于某种原因不满足这两个条件,则我们将其忽略。
- 接下来,我们从传入数据中提取交易数据和指令。我们将所有指令展平为一个数组,以便更方便地处理内部和外部指令。
- 我们检查交易是 Pump.fun 上的买入还是卖出 —— 我们将稍后定义该方法。
- 我们然后从交易数据中提取金额和花费的 SOL。
- 如果交易包括 Pump.fun 的购买:我们调用
handleWhaleBuy()函数,并传递相关交易数据。
你将注意到我们在这里使用了一些尚未定义的辅助方法。让我们现在添加这些。将以下剩余的辅助方法添加到你的机器人类中。
isSubscribeUpdateTransaction = (data) => { return ( "transaction" in data && typeof data.transaction === "object" && data.transaction !== null && "slot" in data.transaction && "transaction" in data.transaction ); }; convertSignature = (signature) => { return { base58: bs58.encode(Buffer.from(signature)) }; }; parseU64 = (data, offset) => { const slice = data.slice(offset, offset + 8); const dataView = new DataView( slice.buffer, slice.byteOffset, slice.byteLength ); return Number(dataView.getBigUint64(0, true)); }; getInstructionData = (instructionData) => { const amount = this.parseU64(instructionData, 8); const solAmount = this.parseU64(instructionData, 16); return { amount, solAmount }; }; getTransactionType = (instructionData) => { if (!instructionData) return "Unknown"; if ( this.config.PUMP_FUN.SELL_DISCRIMINATOR.equals( instructionData.slice(0, 8) ) ) { return "SELL"; } else if ( this.config.PUMP_FUN.BUY_DISCRIMINATOR.equals( instructionData.slice(0, 8) ) ) { return "BUY"; } return "Unknown"; }; matchesInstructionDiscriminator = (ix) => { if (!ix?.data) return false; return ( this.config.PUMP_FUN.SELL_DISCRIMINATOR.equals(ix.data.slice(0, 8)) || this.config.PUMP_FUN.BUY_DISCRIMINATOR.equals(ix.data.slice(0, 8)) ); }; 让我们解释其中每个的作用:
isSubscribeUpdateTransaction():检查传入的数据是否是来自 Yellowstone 的有效交易对象。convertSignature():将交易签名转换为 base58 编码字符串,以便更容易进行日志记录。parseU64():从指令数据中以给定偏移量解析 64 位无符号整数。这样我们就可以获取像代币数量和 SOL 数量的指令数据。getInstructionData():根据程序 IDL 中已知的偏移量从指令数据中提取金额和 SOL 数量。getTransactionType():根据指令数据的标志符确定交易是买入还是卖出,这些标志符包含在我们的配置中(来自程序 IDL 的已知值)。matchesInstructionDiscriminator():检查指令是否匹配买入或卖出的标志符。
初始化我们的机器人
最后,让我们创建一个方法来初始化我们的 Yellowstone 实例,以及一个方法来启动我们的机器人。将以下方法添加到你的 CopyTradeBot 类中:
monitorWhales = async () => { console.log("监控鲸鱼..."); const client = new Client( process.env.YELLOWSTONE_ENDPOINT, process.env.YELLOWSTONE_TOKEN, {} ); const stream = await client.subscribe(); const request = this.createSubscribeRequest(); try { await this.sendSubscribeRequest(stream, request); console.log( "Geyser 连接已建立 - 正在监视鲸鱼 Pump.fun 活动。" ); await this.handleStreamEvents(stream); } catch (error) { console.error("订阅过程中的错误:", error); stream.end(); } }; start = async () => { console.log("🤖 Pump.fun 复制交易机器人正在启动..."); this.monitorWhales(); }; 这些方法做的事情如下:
monitorWhales():使用你在.env中配置的端点和Token建立一个 Yellowstone 客户端。然后创建我们关心的 Pump.fun 交易的订阅,并监听传入数据。最终流式传输数据将传递给handleStreamEvents进行处理。start():通过调用monitorWhales()启动机器人。这是我们的机器人的入口点。
让我们在 bot.js 文件中添加一行最后的代码,以便运行脚本时启动我们的机器人。在 CopyTradeBot 类外部添加以下主函数:
async function main() { const bot = new CopyTradeBot(); await bot.start(); } main().catch(console.error);就是这样!如果你遇到任何问题,你可以在我们的 GitHub 示例库中查看此项目的完整代码, 在这里。让我们测试一下吧!
测试机器人
此时,你的 bot.js 文件包含监控目标钱包或钱包(WATCH_LIST)并在 SOL 数量超过 MIN_TX_AMOUNT 时复制其 Pump.fun 购买的完整逻辑。为了演示/测试,请确保你的 WATCH_LIST 只是你控制的单个钱包地址。我们将在下一步中使用此地址复制自己的交易。
请小心真实资金
此示例在主要网上进行,如果将 TEST_MODE 设置为 false,将执行真实交易。请小心真实资金,并考虑在 本地网络 上测试或使用少量 SOL。链上交易是不可逆的,如果未正确执行,可能会导致资金损失。
- 为你的机器人钱包注资:确保你的机器人钱包(
.env文件中的钱包)拥有足够的 SOL 来支付所有兑换费用和交易费用。 - 运行机器人:
node bot.js- 触发交易:使用来自
WATCH_LIST的相同钱包,访问 Pump.fun 并执行一次购买交易。 - 检查机器人输出:
-你的控制台应打印类似 🎯 - BUY - TxID: ... 的消息 -如果交易被检测到,你应在文件 pump_fun_swaps.json 中看到 COPY_BUY 日志条目。 -如果你以 TEST_MODE 设置为 false 运行机器人,你可以在 Solana Explorer 上查看你的 txid 以获取交易详细信息。

干得好!
继续构建!
恭喜你!你已经构建了一个简单而强大的 Solana 复制交易机器人,使用 Pump.fun API 进行交换和 Yellowstone gRPC 进行实时交易流。这种设置演示了如何订阅链上事件并通过使用 Pump.fun API 获取和发送自己的交换交易来以编程方式做出响应。
随意定制此机器人以实现更高级的策略:- 变量购买金额:而不是固定的 BUY_AMOUNT,你可以跟踪鲸鱼的购买规模与你可用的总 SOL 之间的比例。
- 止损/卖出信号:实施逻辑以检测大量抛售或设置自动卖出代币的阈值。
- 多程序监控:扩展你的 Yellowstone 订阅,以监视 Solana 上的其他 DeFi 协议。考虑利用 Metis API 用于其他 DEX 或 AMM,和/或设置限价单。
感谢你的关注,祝你构建愉快!如果你有任何问题或想探索更多 Solana 和 DeFi 教程,请查看我们的 指南 和 文档。祝你好运,安全交易!
让我们知道你在做什么或是否有任何问题!你可以在 Discord 或 Twitter 与我们联系。
我们 ❤️ 反馈!
让我们知道 如果你有任何反馈或对新主题的请求。我们很想听到你的声音。
资源
- 本指南的完整代码
- Metis - Jupiter V6 Swap API
- Yellowstone gRPC
- JupiterAPI.com(公众 Pump.fun API 端点)
- Solana 指南
- 指南:构建 Jupiter 交易机器人
- 🎥 视频:构建 Jupiter 交易机器人
- 原文链接: quicknode.com/guides/sol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~