给 Solana 交易机器人加上 Token-2022 支持

一个只支持经典代币的机器人硬编码了五处假设,以及按什么顺序修才不会变成静默的半成品。

BoltTx Team··13 min read
solanatoken-2022交易机器人集成交易上链spl-token

一个能正确处理经典 SPL 代币的机器人,不会"部分地"处理 Token-2022。★它在一些代币上完全正常,在另一些上完全失败,中间没有过渡。★

这其实是好消息 —— 因为它意味着这次集成是一份有限的清单,而不是一项持续的税。

五处硬编码的假设

每一个只支持经典代币的机器人都藏着这些,而且通常没意识到:

TOKEN_PROGRAM_ID            // ★被当成常量★
getAssociatedTokenAddress(mint, owner)     // ★漏了 program ID★
{ dataSize: 165 }           // ★假设了账户大小★
minOut = expected * (1 - slippage)          // ★假设全额到账★
createTransferInstruction(...)              // ★只支持经典的构造器★

★每一处在遇到 Token-2022 的 mint 之前都完美工作,然后以一种不会点名这个假设的方式失败。★

按这个顺序修

顺序很重要 —— 乱序修会产出一个以令人困惑的方式半可用的机器人。

1. 从 mint 解析出 program ID。 后面每一步都依赖它可用。

async function resolveToken(connection, mint) {
  const info = await connection.getAccountInfo(mint);
  if (!info) throw new Error("mint 不存在");
  return { programId: info.owner, is2022: info.owner.equals(TOKEN_2022_PROGRAM_ID) };
}

★缓存它 —— 一个 mint 的所属程序永不改变。★ 在你追踪代币的每一处,都把它和 mint 地址存在一起。

2. 把它贯穿到每一次推导和每一个构造器。

const ata = await getAssociatedTokenAddress(mint, owner, false, programId);
const ix = createTransferInstruction(src, dst, owner, amount, [], programId);

★省略这个参数会默认成经典程序★ —— 这就是为什么这个失败在经典代币上完全不可见、在 Token-2022 上是全面的。

如果你的 Token-2022 支持已经完整、交易还是会错过,免费领个 BoltTx key 改一行就能测测提交路径。

3. 两个程序都查余额。

const [a, b] = await Promise.all([
  connection.getParsedTokenAccountsByOwner(wallet, { programId: TOKEN_PROGRAM_ID }),
  connection.getParsedTokenAccountsByOwner(wallet, { programId: TOKEN_2022_PROGRAM_ID }),
]);

4. 交易之前先筛查扩展。 跳过不可转让和永久委托方;为转账费、hook、默认冻结做适配。

5. 最后再改费用算法。 它依赖第 4 步已经读到了配置。

★在第 1 步之前做第 5 步是常见的错误★ —— 你没法为一个还没解析出程序的 mint 计算转账费。

它在延迟上花你多少

对狙击手来说,老实的账是:

多一次账户读取 —— 就是那个 mint,它在一次读取里同时给你 program ID 和扩展数据。★而你多半本来就要为 decimals 读它。★

经典代币不额外花任何时间。 owner 检查立刻短路,所以大多数代币只花你一次比较。

带 hook 的代币要模拟 —— 那是真的贵,也正是这类代币在速度敏感的策略里通常值得跳过的原因。

★这次集成不会拖慢你现有的流程。它加的是一个大多数代币不会走进去的分支。★

测试上的难题

你没法在 devnet 上拿真实代币验证这件事,因为你在乎的那些代币在那里不存在。

行得通的做法:

// ★自己造带各种扩展的测试 mint。★
await createMint(connection, payer, authority, null, 9,
  Keypair.generate(), undefined, TOKEN_2022_PROGRAM_ID);

你声称支持哪个扩展,就为它造一个 mint,然后拿完整交易路径跑一遍。★一个从没对着带费用的 mint 执行过的机器人,它的费用算法就是未经测试的★ —— 不管写得多仔细。

要测卖出,不只是买入。 不可转让代币和选择性 revert 的 hook,在买入时都看起来正常、在退出时才失败 —— 而那是在生产环境发现代价最高的方向。

什么完全没变

值得说清楚,因为它界定了工作量:

★Token-2022 是代币层的变化,不是交易层的。★ 你的重试循环、手续费策略、提交路径都不用动 —— 这就是这次集成是有限的原因。

一份验收清单

□ program ID 从 mint 的 owner 解析,不是假设
□ program ID 传给每一次 ATA 推导
□ program ID 传给每一个指令构造器
□ 余额查询覆盖两个程序
□ ★dataSize 过滤器不假设 165★
□ 交易前筛查扩展
□ ★转账费在滑点之前减掉★
□ hook 账户用异步构造器组装
□ 不可转让、永久委托方直接拒绝
□ ★卖出路径测过,不只是买入★
□ 租金按实际账户大小计算

★带星的几行是会静默失败的。★ 其余的会产出一个大致指向问题的错误。

上链应该是什么水平

我们投递节点上的真实交易:中位确认 336 毫秒 —— 不到一个 slot。

★Token-2022 支持改变的是你的交易正不正确,不是它多快落块。★ 这是两个独立的问题、有各自的修法,而一笔正确的交易仍然要和其它交易一样争夺收录。

BoltTx 在这里的位置

我们负责提交,而 Token-2022 完全没有改变这一块。

提交走我们自建的四区域投递节点,带 SWQoS 路由,不暴露公共 mempool —— 交易在落块之前、传输途中观察不到。你的指令引用经典还是 2022 代币程序,对我们没有区别 —— 我们传输你签好的字节。

你在本地签名。我们不托管资金、不代签、不修改交易内容。tip 带在交易里、从你自己的钱包链上支付,交易失败时跟着一起 revert —— 这是 Solana 原子交易的机制决定的。只有到达链上的交易才计费。

免费领 API key,没有月费:

const connection = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");

常见问题

怎么给现有机器人加 Token-2022 支持? 从 mint 的 owner 解析 program ID、把它贯穿到每一次推导和构造器、两个程序都查余额、筛查扩展,最后调整费用算法。顺序很重要。

经典机器人遇到 Token-2022 代币时最先坏在哪? 通常是 ATA 推导,因为 program ID 是推导的一部分。你会为一个存在于另一个地址的账户拿到 AccountNotFound。

需要重写我的重试和手续费逻辑吗? 不需要。Token-2022 是代币层的变化。 交易结构、blockhash 处理、重试、compute budget 和提交全都不变。

Token-2022 支持会增加多少延迟? 几乎没有。那次 mint 读取在一次请求里同时给你 program ID 和扩展数据,而经典代币在 owner 比较处就短路了。

怎么测试 Token-2022 的处理? 在 devnet 上自己造测试 mint,你支持哪个扩展就造一个,然后跑完整交易路径。带这些扩展的真实代币在 devnet 上不存在。

为什么必须专门测卖出路径? 因为不可转让代币和选择性 revert 的 hook,在买入时都成功、在退出时才失败。只测买入,会把最贵的那种失败留到生产环境。

该支持 transfer hook 吗? 只有当 hook 程序可验证、且双向模拟都干净时才该。对速度敏感的策略,跳过带 hook 的代币通常是更划算的取舍。

哪些扩展该直接拒绝? 不可转让和永久委托方。 一个让你卖不掉,另一个让第三方能拿走你的仓位,两者都没法靠更好的代码解决。

加了 Token-2022 之后,余额查询为什么还是漏代币? 你多半还在只做一次查询。没有合并调用,两个 program ID 需要各查一次,再把结果合并。

需要改我的 dataSize 过滤器吗? 如果它假设了 165 字节,需要。带扩展的账户更大,所以精确大小的过滤会静默排除每一个带扩展的 Token-2022 账户。

滑点里怎么处理转账费? 在套用容忍度之前把费用从预期输出里减掉。 反过来做,会在费用超过滑点时确定性地 revert。

该按 mint 缓存 program ID 吗? 该,而且可以永久缓存。所属程序永不改变,所以它属于你追踪代币的那个结构,和 decimals 放在一起。

如果聚合器帮我处理了 Token-2022 呢? 用一笔小额交易验证,不要假设。 支持程度因协议和扩展而异,transfer hook 尤其需要显式处理。

Token-2022 影响交易体积吗? 间接影响。带 hook 的代币会加账户、每个 32 字节,可能把一条本来装得下的多跳路由顶出 1232 字节上限。

到底值不值得支持 Token-2022? 取决于你交易什么。如果你的目标平台上新用它,值得。 如果不用,干净地筛掉这类代币也是一个有效的替代方案。

最小可用的 Token-2022 支持是什么? 解析 program ID、把它贯穿下去、两个程序都查,并拒绝任何带有你尚未实现的扩展的 mint。这样是安全的,尽管不完整。

延伸阅读

← 返回博客列表