Token-2022 转账钩子脚手架 是做什么的?
转账钩子是 Token-2022 扩展中最难正确实现的扩展:程序必须暴露一个 Anchor 无法生成的接口鉴别器,以字节严格顺序布局
ExtraAccountMetaList,并抵御带外调用。微小的错误会表现为晦涩的运行时错误。本技能是一条开箱即用的安全路径,由可编译的程序和 LiteSVM 攻击测试提供支持。与其他 token-扩展技能的比较:
solana-token-extensions/token-2022-extensions将钩子视为一个特性(一个静态示例,加上一个清单审计)。这是专用的钩子工具——唯一一个可以根据需求生成全新安全钩子(scaffold.sh)并使用通过的攻击测试支持其审计器的工具。它们是互补的:当您实际构建或加固钩子时,可以将其与它们一起安装。
何时使用此技能
- 构建转账钩子:白名单/KYC 门控、版税强制执行、费用路由、每笔转账会计/限额、灵魂绑定类规则。
- 调试钩子:
AccountNotFound、NotEnoughAccountKeys、IncorrectAccountAddress或“转账在没有钩子时有效,但在有钩子时失败”。 - 审查/审计现有钩子程序是否存在常见的陷阱。
- 创建一个指向钩子的 Token-2022 铸币厂,并编写客户端转账指令。
集成者须知:如果您处于消费方(接受一个带有钩子的第三方铸币厂),那是一个不同的问题——大多数集成者应拒绝未知的钩子铸币厂。请参阅配套的 token2022-integration-guard 技能。
思维模型(请先阅读)
- 钩子程序实现了
Execute(SPL transfer-hook 接口),Token-2022 在每次转账时都会 CPI 调用它。Anchor 通过sha256("global:<ix>")进行调度,这不匹配接口鉴别器——因此您添加一个fallback,它解码接口指令并在进程内调度(切勿自我 CPI)。→ program.md - 钩子可以需要额外账户。您在
ExtraAccountMetaListPDA (["extra-account-metas", mint]) 中声明它们一次;Token-2022 读取它并在转账时将这些账户传递给Execute。顺序至关重要。→ extra-account-metas.md - 客户端必须使用
createTransferCheckedWithTransferHookInstruction构建转账(普通的transferChecked会省略额外账户并导致回滚)。→ client-wiring.md - 钩子可被攻击者访问:请保护它。→ security.md
- 通过经过钩子的转账测试来证明它。→ testing.md
路由表
| 用户正在…… | 阅读 |
|---|---|
| 编写钩子程序(Execute、fallback、初始化元列表) | program.md |
| 处理 ExtraAccountMetaList / 不透明的账户错误 | extra-account-metas.md |
| 设置铸币厂 + 客户端转账 | client-wiring.md |
| 加固/审计钩子的安全性 | security.md |
| 为钩子编写测试 | testing.md |
| 搭建一个全新的钩子工作区 | 运行 /transfer-hook:scaffold |
| 审计现有的钩子程序 | 运行 /transfer-hook:audit |
| 来源/延伸阅读 | resources.md |
此技能包含的内容
| 工件 | 路径 | 说明 |
|---|---|---|
| 参考钩子程序 | examples/hook/ | 一个可编译的 Anchor 1.0 钩子:白名单门控 + 每铸币厂计数器、fallback 路由器以及 is_transferring 守卫。 |
| 攻击测试 | examples/hook/tests/ | LiteSVM 测试,经过钩子进行转账:白名单通过(计数器递增)、非白名单被拒绝、直接调用 Execute 被拒绝。 |
| 脚手架脚本 | scripts/scaffold.sh | 从参考实现生成一个全新的钩子工作区。 |
固定技术栈 (2026)
Anchor 1.0、anchor-spl 1.0.2、spl-transfer-hook-interface =2.1.0、spl-tlv-account-resolution 0.11、@solana/spl-token 0.4.14、litesvm 1.2(Kit 原生,已预加载 Token-2022)。参考程序可编译,其测试在此技术栈上通过。
不可妥协的规则
- 添加一个
fallback,用于解码TransferHookInstruction::Execute并在进程内调度(__private::__global::transfer_hook)——切勿invoke您自己的程序。 - 在首次转账前初始化
ExtraAccountMetaList,并保持Vec<ExtraAccountMeta>的顺序与您的Execute账户结构完全一致。 - 使用
is_transferring守卫Execute——拒绝来自真实转账之外的调用。 - 将每个注入的账户绑定到 PDA 种子(
Account<T>+seeds),不要使用裸UncheckedAccount,否则攻击者可以伪造您的白名单。 - 保持钩子最小化和全功能——它会在每次转账时运行;一次 panic/高 CU 会破坏代币。
- 测试转账路径,而不仅仅是单元逻辑——一个可以编译的钩子可能在解析时仍会失败。