关于 pump.fun API,第一件要知道的事是:它没有官方 API。没有文档化的 REST 端点,没有发布的 SDK,没有支持渠道。
大家说"pump.fun API"的时候,指的其实是三件不同的事。你需要哪一件,取决于你是在读状态、检测新币、还是真的要交易。
三种含义
一、读 bonding curve 状态
每个 pump.fun 代币都有一个链上账户存着曲线状态 —— 虚拟储备、真实储备、是否已完成。读它的方式和读任何 Solana 账户一样:对程序的 PDA 调 getAccountInfo,然后反序列化。
★不需要 API,它就是链上状态。★ 真正的工作在于知道账户布局、以及把数学算对。
二、检测新币上线
你想在别人之前知道某个代币出现了。三个选择:轮询程序下的新账户、订阅程序日志、或者用第三方 feed(别人做了前两件事然后转售)。
轮询最简单也最慢。日志订阅更快但要自己写解析。第三方 feed 接入最快,但多了一跳你控制不了的环节。
三、提交交易
构造 swap 指令并把它送进区块。★这里才是真正决定赚钱还是亏钱的地方★,而且它和 pump.fun 没关系 —— 这是一个标准的 Solana 交易提交问题。
已经在写了、只缺提交那一半?免费领个 BoltTx key 改一行配置就行。这篇剩下的部分讲的是你得自己写的那些。
读曲线状态
bonding curve 账户里有四个关键数字:
import { Connection, PublicKey } from "@solana/web3.js";
const PUMP_PROGRAM = new PublicKey(
"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P",
);
function bondingCurvePda(mint: PublicKey): PublicKey {
const [pda] = PublicKey.findProgramAddressSync(
[Buffer.from("bonding-curve"), mint.toBuffer()],
PUMP_PROGRAM,
);
return pda;
}
async function readCurve(connection: Connection, mint: PublicKey) {
const info = await connection.getAccountInfo(bondingCurvePda(mint));
if (!info) return null;
// 布局:8 字节 discriminator,然后五个 u64 和一个 bool。
const d = info.data;
const u64 = (off: number) => d.readBigUInt64LE(off);
return {
virtualTokenReserves: u64(8),
virtualSolReserves: u64(16),
realTokenReserves: u64(24),
realSolReserves: u64(32),
tokenTotalSupply: u64(40),
complete: d[48] === 1, // 曲线毕业后变成 true
};
}
任意时刻的价格由虚拟储备按恒定乘积算出来:
// 买入 tokensOut 个代币需要多少 lamport(未含手续费)。
function costToBuy(
virtualSol: bigint,
virtualTokens: bigint,
tokensOut: bigint,
): bigint {
if (tokensOut >= virtualTokens) throw new Error("超出曲线");
const k = virtualSol * virtualTokens;
const newTokens = virtualTokens - tokensOut;
return k / newTokens - virtualSol + 1n; // +1 向曲线一侧取整
}
★两个最容易踩的坑:★ 储备是 u64,全程必须用 BigInt;还有 complete 这个标志很重要 —— 曲线一旦完成,交易就转到 AMM 池,这套数学不再适用。
检测新币
三种做法,各自的取舍:
轮询 getProgramAccounts。 拉取程序下所有账户,和上次比对差异。简单,但重到大多数服务商都会限流。你会慢几秒。
日志订阅。 通过 WebSocket 订阅程序日志,边流边解析创建事件。快得多,解析要自己写。
const sub = connection.onLogs(
PUMP_PROGRAM,
(logs) => {
// 创建事件出现在日志行里。解析之后要再取一次账户拿权威状态,
// 光看日志不够。
if (logs.logs.some((l) => l.includes("Instruction: Create"))) {
handleNewMint(logs.signature);
}
},
"processed", // 用 "confirmed" 会白白多花一个 slot 以上的延迟
);
第三方 feed。 别人跑上面那套然后把结果卖给你。接入最快,但你继承了他们的延迟,还多了一跳。
★不管选哪个,检测只是这场比赛的一半。★ 第一个知道,但交易比别人晚三个 slot 落块,一分钱也拿不到。
钱实际是在哪一步动的
这一段最容易被低估。假设你检测到新币、决定买入,流程是:
检测 → 构造指令 → 签名 → 提交 → 落块
大多数教程把前三步讲得很细,最后两步一句"调 sendTransaction"带过。但在上新的那一刻,★网络恰恰因为所有人都在做同一件事而拥堵★,提交就成了瓶颈。
决定你能不能成交的三件事:
按实时行情算 priority fee。 写死的值要么浪费、要么不够,而上新时通常是不够。
import { ComputeBudgetProgram } from "@solana/web3.js";
// 费用是按账户竞争的。要传你这笔交易实际会写入的账户,
// 而不是全局查一个数。
const recent = await connection.getRecentPrioritizationFees({
lockedWritableAccounts: [bondingCurvePda(mint), yourTokenAccount],
});
const fees = recent.map((r) => r.prioritizationFee).sort((a, b) => a - b);
const median = fees[Math.floor(fees.length / 2)] ?? 0;
instructions.unshift(
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: Math.max(median * 3, 10_000), // 上新时竞争激烈
}),
ComputeBudgetProgram.setComputeUnitLimit({ units: 120_000 }),
);
持续重试直到 blockhash 过期。 上新时单次提交就是掷硬币。
一条不会被降级的提交路径。 验证节点按转发方的质押权重来接收转发过来的交易。拥堵时,低质押路径恰好在你最不希望的时候掉链子。
滑点:曲线一直在动
bonding curve 的价格每有一笔买入就会变,这意味着你算出报价的那一刻它就已经过时了。上新活跃时,从报价到收录之间可能动好几个百分点。
滑点要按你读到的曲线状态来设,而不是对一个本来就过时的价格再取一个固定百分比:
const curve = await readCurve(connection, mint);
const expectedCost = costToBuy(
curve.virtualSolReserves,
curve.virtualTokenReserves,
tokensWanted,
);
// maxSolCost 是进指令的那个值。设太紧,曲线一动就失败;
// 设太松,别人先把价格推上去你就多付钱。
const maxSolCost = (expectedCost * 115n) / 100n;
★滑点太紧导致交易失败,基础费照付。★ 成交在一个糟糕的价格上,亏得更多。两个都不是免费的,所以容忍度是一个真实的决策,不是一个可以不管的默认值。
毕业之后什么变了
曲线完成后,代币迁移到 AMM 池。那一刻你的集成里几乎所有东西都变了:
| 毕业前 | 毕业后 | |
|---|---|---|
| 价格来源 | bonding curve 账户 | AMM 池储备 |
| 指令 | pump.fun buy/sell | 标准 AMM swap |
| 滑点行为 | 随曲线变动 | 随池子深度变动 |
★一个不检查 complete 标志的机器人,会对着已毕业的代币继续发曲线指令,然后稳定失败。★ 每笔交易前都要检查,不是启动时查一次就完了。
上链应该是什么水平
我们投递节点上的真实交易:中位确认 336 毫秒 —— 不到一个 slot。
平常这个数字看不出差别,拥堵时才会把各条提交路径拉开。而 bonding curve 上真正有钱赚的那几分钟,恰好就是拥堵最严重的时候。
BoltTx 在这里的位置
我们只做最后一步,别的都不做。不做索引,不做解析历史,不做上新 feed。
提交走我们自建的四区域投递节点,带 SWQoS 路由,不暴露公共 mempool —— 交易在落块之前,传输途中观察不到。你在本地签名,我们不托管资金、不代签、不修改交易内容。
tip 带在交易里,从你自己的钱包链上支付。交易 revert 的话 tip 跟着一起 revert —— 这是 Solana 原子交易的机制决定的。只有到达链上的交易才计费。
免费领 API key,没有月费:
// 选离你机器人最近的区域
const connection = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");
常见问题
pump.fun 有官方 API 吗? 没有。没有文档化的 REST 端点、没有发布的 SDK、也没有支持渠道。大家说的 pump.fun API,是"读链上程序账户 + 订阅程序日志 + 基于这两者的第三方 feed"的混合体。
pump.fun 的 program ID 是什么?
主网是 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P。bonding curve 的 PDA 由种子 bonding-curve 加 mint 地址在该程序下推导出来。
怎么读 pump.fun 的 bonding curve?
用 getAccountInfo 取 bonding curve PDA,然后反序列化账户数据。跳过 8 字节 discriminator 之后是五个 u64(虚拟储备、真实储备、总供应量)和一个表示曲线是否完成的布尔值。
pump.fun 代币的价格怎么算?
对虚拟储备用恒定乘积:k = virtualSol * virtualTokens,然后解出取走目标数量代币需要多少 SOL。全程用 BigInt,因为储备是 u64,JavaScript 的 number 会丢精度。
怎么检测 pump.fun 的新币上线?
三个选择:轮询 getProgramAccounts 做差异比对(简单、慢、容易被限流)、通过 WebSocket 订阅程序日志并解析创建事件(更快、要自己写解析)、或者用第三方 feed(接入最快,多一跳)。
pump.fun 有 SDK 吗? 没有官方的。社区有几个库封装了指令构造和账户布局。依赖之前先读源码 —— 布局会变,而一个没人维护的封装会静默失败。
我的 pump.fun 狙击交易为什么总失败? 通常是四个原因之一:priority fee 不够应对上新时的拥堵、构造到提交之间 blockhash 过期了、滑点容忍度对一条正在动的曲线来说太紧、或者提交路径在高负载下被降级。
pump.fun 交易的滑点该设多少? 先按当前曲线状态算出预期成本,再加容忍度。上新活跃时,价格在报价到收录之间会动,所以设太紧会经常失败。而失败的交易基础费照付,所以这是个真实的取舍。
怎么知道一个 pump.fun 代币已经毕业了?
看 bonding curve 账户里的 complete 布尔值。一旦为 true,交易已经转到 AMM 池,曲线指令会失败。每笔交易前都要查,不是启动时查一次。
pump.fun 机器人能用普通的 Solana RPC 吗? 读状态可以。但上新时提交的瓶颈不是你 RPC 的读取容量,而是拥堵下你的交易怎么被路由到出块方。
狙击一个 pump.fun 上新需要多快? 快到能在检测后一两个 slot 内落块。检测速度没大多数人以为的那么重要 —— 第一个知道,但交易比别人晚三个 slot 落块,一样拿不到。
pump.fun 买入该设多少 compute unit limit? 开发阶段模拟一次拿到真实消耗,然后设一个略高的值。用默认值意味着按一个高得多的数字计费,而这恰好浪费在费用最贵的那些时刻。
为什么我的机器人测试时好好的,真上新就挂? 测试时网络清闲,所有提交路径表现都一样。上新会造成拥堵,而拥堵时 priority fee、重试行为、路由质量会同时开始起作用。
pump.fun 机器人需要 WebSocket 连接吗? 做上新检测的话,比轮询快得多。提交不需要 —— 那是一个 HTTP 调用。很多机器人是 WebSocket 做检测、另一个专做投递的端点做发送。
现在做 pump.fun 狙击还有得赚吗? 竞争很重,利润取决于执行而不是策略。做得好的团队把提交当成核心问题,而不是事后补的一环 —— 因为剩下的边际优势就在那里。