swap 失败时的标准建议是调高滑点。它成功的次数足够多,多到变成一种习惯 —— 也因此掩盖了滑点从来就不是问题的那种情况。
一笔因滑点失败的 swap,和一笔因为迟到而失败的 swap,从外面看长得一模一样:你没成交。而这两者的修法是相反的。
滑点容忍度到底是什么
滑点容忍度不是一个偏好设置。它是被写进指令里的最小输出量,由程序在链上强制执行。
// "1% 滑点"最终变成的东西。
const minimumOut = expectedOut * (1 - 0.01);
交易执行时,程序根据当前池子状态算出真实输出。★如果低于 minimumOut,程序就中止,交易 revert。★
这有三个值得直说的后果:
这个检查发生在执行时,不是提交时。 等你的交易真正运行时,你报价时依据的那个价格早就是历史了。
滑点失败是一笔已经落块的交易。 它到了链上、执行了、然后 revert 了。基础手续费你付了。
调高容忍度拓宽的是"你愿意接受什么",不是"你多快到达"。 这跟时间是两条不同的轴。
区分两者的诊断方法
在动滑点数字之前,先搞清楚你遇到的是哪一种失败:
const status = await connection.getSignatureStatus(sig);
if (!status.value) {
// ★根本没落块。滑点在这里完全不相关。★
// 去看手续费、重试行为、blockhash 过期。
} else if (status.value.err) {
// ★落块了并且 revert 了。这时候才该读错误。★
const tx = await connection.getTransaction(sig, {
maxSupportedTransactionVersion: 0,
});
console.log(tx?.meta?.logMessages?.slice(-10));
}
★如果 status.value 是 null,调高滑点什么都改变不了。★ 交易根本没执行,所以最小输出检查压根没跑过。这种情况下调高容忍度只做了一件事:让那些真正落块的交易成交更差。
这是这个领域里最常见的误诊,而且它的代价是安静的 —— 你留着真正的问题,并且在每一笔成功的交易上为它付钱。
如果你的 revert 确实是滑点、而其余那些是迟到,免费领个 BoltTx key 改一行就能测测时间那一半。
读懂 revert
滑点失败会在日志里自报家门,措辞因程序而异:
Program log: Error: slippage tolerance exceeded
Program log: exceeds desired slippage limit
custom program error: 0x1771
★Anchor 程序的自定义错误从 6000 开始编号★,所以 0x1771 是 6001 —— 该程序枚举里的第二个错误。光有这个数字、没有程序的 IDL 是没有意义的,但它确实告诉你:这次失败是程序主动做的检查,而不是运行时故障。
值得和下面这些区分开,它们都不是滑点:
insufficient funds—— 余额或 ATA 问题exceeded CUs meant to be used—— 计算上限设低了Blockhash not found—— 执行前就过期了AccountNotFound—— 你指令里的某个账户还不存在
怎么选这个数字
没有一个放之四海皆准的容忍度,但有一个正确的方法:从池子推导出来,而不是靠习惯。
// 价格冲击是"你这个规模对上这个池子"的属性。
const impact = (spotOut - expectedOut) / spotOut;
// 容忍度要覆盖冲击,加上你在途期间的价格移动。
const tolerance = Math.abs(impact) + volatilityBuffer;
按场景的大致区间:
| 场景 | 常见容忍度 |
|---|---|
| 深池、稳定交易对 | 0.1% – 0.5% |
| 普通 meme 交易 | 1% – 3% |
| ★新盘、流动性很薄★ | ★5% – 15%★ |
| 砸盘时出货 | 更高,并接受这个代价 |
★最后一行是一个真实的决策,不是一个设置。★ 当你必须出来时,成交得难看好过没成交。而当你在进场时,成交难看就只是一笔糟糕的交易 —— 没有"紧急"这个理由。
路由改变的是你的失败方式
聚合器把一笔 swap 拆到多个池子上以拿到更好的价格。这确实改善了那个数字,同时也改变了会出什么问题。
账户更多。 每个池子都带着自己的金库和 authority,每个账户 key 占 32 字节。一条多跳路由可能逼近 1232 字节的交易上限。
计算更多。 每一跳都消耗计算单元,所以一条报价漂亮的路由,可能需要比你设的更高的 limit。
revert 的方式更多。 任何一跳没通过自己的检查,整笔交易都会 revert,因为它是原子的。
报价更陈旧。 跳数越多,意味着越多池子的状态会在报价和执行之间发生变化。
// ★有时候跳数更少才是更划算的那笔交易。★
const quote = await getQuote({
inputMint, outputMint, amount,
maxAccounts: 32, // 给交易体积设个上界
onlyDirectRoutes: false, // 当可靠性优先于价格时设 true
});
★onlyDirectRoutes: true 被用得太少了。★ 抢跑的时候,一条能落块的直接路由,胜过一条会 revert 的拆分路由。拆分带来的价格改善很小;完全没成交的代价,是整笔交易。
新盘上的滑点
打新是常规推理失效的地方。
最初几秒里价格移动极大,所以收紧容忍度等于保证 revert。但在一个很薄的池子上放宽容忍度,意味着你自己的单子把价格推得对你不利,最后成交在一个你本来不会选的位置。
★而且你的容忍度还有第二个读者。★ 在一条公开可见的路径上设一个很宽的最小输出量,等于明确告诉所有盯着的人:他们有多大空间可以抢在你前面交易,并且仍然让你的交易保持有效。宽容忍度只有在"交易落块之前不可被观察"的路径上才安全。
几个实操要点:
缩小规模,而不是放宽容忍度。 一半的规模、一半的冲击,通常好过双倍的容忍度。
用池子状态,不要用报价。 打新时,一秒钟之前的报价描述的是另一个池子。
预期会有 revert,并把它算进预算。 打新时相当一部分尝试会 revert。这是"早"的成本,也是基础手续费在这种场景下比平时更重要的原因。
上链应该是什么水平
我们投递节点上的真实交易:中位确认 336 毫秒 —— 不到一个 slot。
★提交到执行之间的每一个 slot,都是价格朝着不利于你的最小输出量移动的时间。★ 落得更快不能替代正确的容忍度,但它确实意味着你的容忍度需要吸收的东西更少了。
BoltTx 在这里的位置
我们不做报价、不做路由、不替你选滑点。那些是你的决策和你的代码。
我们负责签名之后的那一跳。提交走我们自建的四区域投递节点,带 SWQoS 路由,不暴露公共 mempool —— 交易以及它里面的最小输出量,在落块之前、传输途中观察不到。
你在本地签名。我们不托管资金、不代签、不修改交易内容 —— 这也包括绝不调整你的滑点。tip 带在交易里、从你自己的钱包链上支付,交易失败时跟着一起 revert —— 这是 Solana 原子交易的机制决定的。只有到达链上的交易才计费。
免费领 API key,没有月费:
const connection = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");
常见问题
Solana 上的滑点容忍度是什么意思? 它会变成写进 swap 指令里的最小输出量。程序在执行时算出真实输出,低于这个最小值就 revert —— 所以它由链上强制执行,不是由你的客户端。
我的 Solana swap 为什么老是滑点失败? 要么是相对池子深度和你的交易规模,容忍度设得太紧;要么是价格在报价和执行之间确实动了。看一下提交到落块的 slot 距离 —— 间隔越长,价格能动的时间越多。
是不是一直调高滑点直到 swap 成功就行? 只有在确认交易确实落块了之后才行。如果它根本没落块,最小输出检查压根没跑,调高容忍度只会让那些真正成功的交易成交更差。
怎么区分滑点失败和根本没落块?
用 getSignatureStatus。返回 null 说明它从没执行过,滑点不相关。返回结果里带 err 说明它落块并 revert 了 —— 这时候再读日志,找出是哪个检查没过。
custom program error 0x1771 是什么意思? 它是某个 Anchor 程序里的 6001 号错误,因为 Anchor 的自定义错误从 6000 开始编号。在很多 swap 程序里这个区间覆盖滑点检查,但要确定还得看程序的 IDL。
Solana 上失败的 swap 要花钱吗? 如果它落块并 revert 了,要 —— 你为这次执行付了基础手续费。如果它根本没落块,不消耗手续费,但它消耗了你的机会。
pump.fun 代币该用多少滑点? 比成熟交易对高,因为早期流动性薄、价格动得快。通常缩小规模比继续放宽容忍度更好,因为你自己的单子也是冲击的一部分。
滑点调高能让交易更快落块吗? 不能。容忍度影响的是程序接不接受这个结果,不是你的交易多快到达出块方。落块速度来自手续费、路由和重试行为。
该用直接路由还是拆分路由? 拆分路由报价通常更好。直接路由体积更小、消耗计算更少、revert 的方式也更少。 抢跑的时候,可靠性一般压过那点价格改善。
我的多跳 swap 为什么超出交易体积上限?
每个池子都会加上账户 key、每个 32 字节,而 Solana 把交易上限定在 1232 字节。在报价请求里限制 maxAccounts,或者用 address lookup table 压缩这些引用。
滑点设置能保护我不被抢跑吗? 部分能。收紧容忍度会限制别人能把价格推动多少、还让你的交易保持有效。 但在一条公开可见的路径上设很宽的容忍度,等于明确公告了这个空间有多大。
报价看着没问题,我的 swap 为什么 revert 了? 报价描述的是报价那一刻的池子状态。从那时到执行之间,其它交易改变了这个状态。在波动大的交易对上,几个 slot 之前的报价描述的是另一个池子。
多跳 swap 需要多少计算? 比单跳多,而且随池子数量增长。模拟你实际打算发送的那条路由,按测出来的数值加余量设置计算上限。
revert 的 swap 和失败的交易是一回事吗? 它是失败交易的其中一种。revert 意味着它落块了、然后被某个程序检查拒绝了。根本没落块是另一种失败,原因不同,修法也不同。
进场和出场的滑点该不一样吗? 通常该。出场时,成交难看好过不成交,用更高的容忍度是站得住脚的。进场时没有"紧急"这个理由,所以宽容忍度就只是在接受更差的价格。
怎么用程序算出滑点? 按你的规模对当前池子的价格冲击推导,再加上在途期间价格移动的余量。一个套用到所有池子的固定数字,要么在薄流动性上太紧,要么在深池上太松。
延伸阅读
- Solana 交易模拟
- Solana 交易错误码
- Solana 版本化交易
- Solana 新币上线狙击
- Solana 交易上链完全指南