哪些 token extension 真的会改你的交易代码

Token-2022 有十几个扩展。四个会改变你怎么构建交易,三个会改变你能做什么,其余的只是外观。

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

Token-2022 带了十几个扩展。大多数指南把它们等权重地列一遍 —— 而当你要判断一个代币能不能放心交易时,那份清单没有用。

★按"它对你的代码做了什么"来排,这份清单会短得多。★

会改变你的交易的四个

扩展 忽略它会坏在哪
★转账费★ ★最小输出检查每次都 revert★
★transfer hook★ ★缺账户、多计算、多一层 CPI★
★不可转让★ ★你卖不掉 —— 仓位被困住★
★默认账户状态★ ★新账户一创建就是冻结的,不能用★

这四个值得为它们写代码。 清单上其余的,要么只影响展示,要么对交易机器人不可见。

"默认账户状态"值得它很少得到的那份关注。 一个把默认设成 frozen 的 mint,意味着为它创建的每一个代币账户在被冻结权限方解冻之前都不能用。★你的买入落块了、账户存在、余额也在,而你动不了它。★

会改变"你能做什么"的三个

永久委托方(permanent delegate)。 一个无需 owner 签名、就能从任何持有该代币的账户转出或销毁的地址。★别人可以随时拿走你的仓位。★ 它对受监管资产是正当机制,在其它任何地方都是明显的危险

mint 关闭权限。 持有它的人能把这个 mint 关掉。通常不是交易层面的顾虑,但它说明这个代币不是按"永久存在"设计的

计息(interest-bearing)。 ★只影响展示。★ 它改变显示出来的数量而不改变原始余额,所以 uiAmount 会和 amount 分岔。在原始单位上做算术 —— 而你本来就该这么做。

读出一个 mint 上的扩展

import { getMint, getExtensionTypes } from "@solana/spl-token";

const info = await connection.getAccountInfo(mintAddress);
if (!info.owner.equals(TOKEN_2022_PROGRAM_ID)) {
  // ★经典 mint —— 不可能有扩展。到此为止。★
  return { extensions: [] };
}

const mint = await getMint(connection, mintAddress, "confirmed", TOKEN_2022_PROGRAM_ID);
const types = getExtensionTypes(mint.tlvData);

★先做 owner 检查不只是一个优化。★ 对一个经典 mint 用 Token-2022 程序去调 getMint 会失败,所以这道检查正是让后面的代码可以安全运行的东西。

扩展以 TLV 数据的形式存在基础 165 字节之后 —— 这就是带扩展的账户更大、以及 dataSize: 165 过滤器会静默漏掉它们的原因

如果你的扩展处理是对的、交易还是会错过,免费领个 BoltTx key 改一行就能测测提交路径。

一个筛查函数

const BLOCKING = new Set([
  ExtensionType.NonTransferable,
  ExtensionType.PermanentDelegate,
]);

const NEEDS_HANDLING = new Set([
  ExtensionType.TransferFeeConfig,
  ExtensionType.TransferHook,
  ExtensionType.DefaultAccountState,
]);

function screen(types) {
  const blocking = types.filter((t) => BLOCKING.has(t));
  const handling = types.filter((t) => NEEDS_HANDLING.has(t));
  return {
    tradeable: blocking.length === 0,
    blocking,
    handling,                       // ★要改代码,不是跳过★
  };
}

★"直接排除"和"需要处理"这个区分,才是有用的那个。★ 转账费不是跳过一个代币的理由 —— 它是调整滑点算法的理由。 而永久委托方是完全另一类,因为再正确的代码也保护不了你

权限比当前状态更重要

★你今天筛过的配置,明天可能就变了。★

权限 它之后还能做什么
转账费配置 ★调高费率★
transfer hook ★指向另一段代码★
冻结 ★冻结你的账户★
永久委托方 ★拿走你的代币★
mint 增发供应量

要检查每一项权限是不是 null,而不只是看当前值是多少。一个费率为零、但配置权限还活着的代币,是一笔可以在任何 epoch 边界冒出来的费用 —— 而它造成的 revert 会显得突然且无法解释。

// ★权限为 null,意味着这项设置被永久冻结了。★
const feeCfg = getTransferFeeConfig(mint);
const canChange = feeCfg != null && feeCfg.transferFeeConfigAuthority != null;

★这和检查 mint authority 判断增发风险是同一个思路★ —— 你问的是"谁还能做什么",不是"谁做过什么"

筛查在时间上花多少

对打新狙击来说,每一次检查都是你花不起的延迟

一次账户读取回答第一个问题。 mint 的 owner 告诉你这一切适不适用 —— 而对大多数代币,答案是不适用

扩展来自同一次读取。 TLV 数据就在你已经读到的那个 mint 账户里,所以解析它们不需要额外往返

★所以实际成本是一次你本来大概就要做的读取。★ 它覆盖不到的是 hook 的行为 —— 那需要模拟,而那才是昂贵的部分,这也是带 hook 的代币在速度敏感的策略里通常值得跳过的原因

上链应该是什么水平

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

★一次与扩展有关的失败会落块并 revert,让你付掉基础手续费。★ 筛查把这份成本从生产环境挪到了一次账户读取上 —— 这就是做筛查的全部理由。

BoltTx 在这里的位置

我们负责提交。一个代币带哪些扩展、你怎么处理它们,完全在你的指令构建里决定

提交走我们自建的四区域投递节点,带 SWQoS 路由,不暴露公共 mempool —— 交易在落块之前、传输途中观察不到。我们不修改交易内容,这包括绝不改动金额或账户。

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

免费领 API key,没有月费:

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

常见问题

Solana 的 token extension 是什么? Token-2022 的 mint 可以携带的可选特性,以 TLV 数据存在基础账户布局之后。范围从转账费、hook,到计息这类只影响展示的特性。

哪些 token extension 影响交易机器人? 转账费、transfer hook、不可转让、默认账户状态,这四个改变你怎么构建交易。永久委托方则改变"你该不该持有这个代币"。

怎么列出一个 mint 上的扩展? 先确认 mint 的 owner 是 Token-2022 程序,用 getMint 取出来,再对它的 TLV 数据调 getExtensionTypesowner 检查必须在前,否则 getMint 会失败。

不可转让扩展是什么? 一项完全禁止转账的 mint 设置。如果你拿到了这样的代币,你卖不掉它 —— 所以它是一个直接排除条件,不是一个可以处理的情况。

永久委托方是什么? 一个无需 owner 签名、就能从任何账户转出或销毁代币的地址。它对受监管资产是正当的,对其它任何东西都是严重危险

默认账户状态做什么? 它决定新创建的代币账户是不是一开始就冻结。默认冻结时,你的买入落块了、余额也在,但在冻结权限方解冻之前你动不了它。

计息扩展会改变我的余额吗? 不会,只改变显示出来的数量。原始 amount 不变,所以 uiAmount 会和它分岔。在原始单位上做算术就完全避开了这个偏差。

getMint 为什么在某些 mint 上失败? 多半是你对一个经典 mint 传了 Token-2022 的 program ID。先读 mint 账户的 owner 再分支,然后才调任何 Token-2022 的辅助函数。

我的 dataSize 过滤器为什么漏掉带扩展的账户? 扩展存在基础 165 字节之后,让账户变大了。按恰好 165 过滤,会静默排除掉每一个带扩展的账户。

该检查权限还是只看当前设置? 都要,但权限更重要。一个费率为零、配置权限还活着的代币,可以在任何 epoch 边界变成真实费用,而由此产生的 revert 毫无预警。

怎么知道一项设置是永久的? 对应的权限为 null。 权限为 null 意味着没有人能再改这项配置 —— 这是唯一真正可得的保证。

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

筛查扩展会拖慢狙击吗? 几乎不会。mint 的 owner 和它的扩展数据来自同一次你多半本来就要做的读取。 只有 hook 的行为需要模拟,而那才是真正昂贵的。

mint 创建之后还能加扩展吗? 扩展在 mint 创建时确定,但它们的参数在相关权限还存在时可以改变。这就是权限检查比"当前值快照"更重要的原因。

所有扩展都要额外租金吗? 任何存储数据的扩展都会增加账户大小、因而抬高租金豁免下限。用实际数据长度去查这个需求,不要假设。

一个代币同时有转账费和 hook 怎么办? 为费用调整最小输出、用支持 hook 的指令构造器补齐账户、并用模拟来定计算上限。 两者互相独立,而且都必须处理。

延伸阅读

返回博客列表