白皮书 · 07
钩子与参考模块
如何在不触及标准的前提下为符合一致性要求的代币添加功能:钩子约定、参考钩子、配套扩展,以及如何编写、测试和注册自己的钩子。
模块只是演示,而非标准
A-REX Core 与其模块之间的关系,就是 ERC-3643 与 T-REX 合规模块之间的关系:模块接入所经由的接口是标准化的;模块则不是。一个没有任何钩子的、符合一致性要求的代币,是有效的 A-REX 代币。任何人编写的钩子,都与每一个符合一致性要求的代币兼容。这正是生态系统在标准不变的情况下得以增长的原因。
graph LR
T["ARexToken<br/>符合一致性要求的代币"] -->|"check / onExecuted / canSend / canReceive"| H1[EligibilityHook]
T --> H2[ExpiryHook]
T --> H3[AttestationHook]
T --> H4["自定义钩子"]
H3 --> A[ARexAttestations]钩子约定
| 回调 | 时机 | 必须 |
|---|---|---|
check | 余额变动之前,针对每个受影响的 id,钩子按注册顺序执行,遇到第一个非零值即停止 | 是视图函数;绝不因政策原因回滚 |
onExecuted | 余额变动之后,针对每个 id,按顺序 | 不得调用代币中会改变余额的函数(代币已被锁定) |
canSend / canReceive | 每次转移时,以及用于代币的 ERC-7943 视图 | 是视图函数 |
op 是 OP_MINT (1)、OP_TRANSFER (2)、OP_BURN (3)、OP_FORCED_TRANSFER (4)、OP_TRANSFORM (5) 之一。在转化中,输入以 to == 0 到达,输出以 from == 0 到达。data 是 ERC-1155 的转移数据;对于 transform 为 abi.encode(processRef),对于强制转移为空。
调用方代币即 msg.sender。需要批次信息的钩子读取 IARexBatch(msg.sender).batchOf(id)。
参考钩子
EligibilityHook:白名单、黑名单、制裁
演示 HOOK_ELIGIBILITY。维护一个可选的白名单、一个黑名单和一个制裁名单;canSend/canReceive 返回它们的合取;check 返回 SANCTIONED、SENDER_INELIGIBLE 或 RECEIVER_INELIGIBLE,以便界面能够解释拒绝的原因。强制转移始终被允许:从被制裁账户手中扣押货物,正是强制执行的意义所在。在生产环境中,这些名单由身份注册表(ONCHAINID、EAS、可验证凭证)或制裁预言机提供数据;钩子并不关心是哪一种。
ExpiryHook:无需模块的易腐性
无状态。从批次中读取 expiresAt 和 producedAt。阻止已到期批次的铸造和转移(BATCH_EXPIRED),但不阻止销毁(核销)或强制转移(强制执行)。可选地阻止尚未生产的批次的转移(BATCH_NOT_PRODUCED)——为收获前融资的平台将该标志设为允许转移;现货市场则将其设为阻止转移。这一个钩子涵盖了易腐性中的阻止部分;衰减曲线则成为一个视图辅助函数或链下估值。
AttestationHook:按商品的监管门槛
把商品代码映射到所需的证明主题。转移之前,它读取批次、查找要求、向证明注册表询问 isAttested,若结果为否则返回 ATTESTATION_MISSING。“咖啡需要有 EUDR 尽职调查引用”“带有机标签的批次需要有效的有机认证证书”“牛需要卫生证书”,各自只需调用一次 setRequirement。
PerishabilityHook:衰减模型
按商品代码配置衰减,并可按批次覆盖;模型有 NONE、LINEAR(衰减至到期时的下限)、EXPONENTIAL(半衰期)和 STEP(每个区间下降一次)。视图函数有 valueBps、secondsRemaining、isExpired。阻止已到期批次的铸造和转移(BATCH_EXPIRED),并阻止剩余价值为零的批次的转移(PERISH:NO_VALUE);绝不阻止销毁或强制执行。
SeasonalHook:收获窗口与交易窗口
按商品和操作设置绝对的 [start, end] 窗口(每个键最多 16 个)。没有窗口的商品/操作不受限制。典型用法:OP_MINT 限于收获窗口,OP_TRANSFER 限于交易窗口;其余时间返回 SEASON_CLOSED。
DeliveryHook:保管与结算
发送方安排通过承运方向接收方交付 amount 个单位;这些单位被锁定(O(1) 计数器,因此 check 绝不会遍历历史记录)。承运方和传感器记录提货、运输和温度读数(越限标志);接收方确认交付;争议提交给仲裁方。结算就是 ERC-1155 转移本身:发送方 → 接收方,数量精确,且 data = abi.encode(deliveryId);只有在交付状态为 DELIVERED 时,check 才会允许,onExecuted 将其标记为 SETTLED 并解锁。自由(未锁定)的单位可继续移动。
TraceabilityHook:EPCIS 形态的事件日志
观察每一次代币操作,并以 CBV 业务步骤(commissioning、shipping、destroying、transforming)和处置状态(active、in_transit、destroyed、seized)对其分类;RECORDER_ROLE 记录非代币的关键追踪事件(冷却、包装、收货),并带有 readPoint 和 bizLocation。有两种模式:仅发出事件(每次转移约增加 17 k Gas;由索引器重建日志)或持久化(约增加 184 k;getEvents 在链上应答,支持分页)。
配套扩展
有三类关注点从来不需要成为转移规则;它们是读取或写入证明的普通合约:
| 扩展 | 作用 |
|---|---|
CommodityPriceOracle | 报告方按商品提交价格;轮次带编号,由法定人数以中位数最终确定,或在达到最低数量后手动最终确定;latestAnswer、isStale |
CarbonImmobilizationWrapper | 记录在登记簿中被锁定的信用(登记簿、序列号范围、年份、吨数、证明),把吨数关联到批次,同步注销和解除锁定,写入证明 AREX:CARBON:IMMOBILIZED;绝不铸造信用 |
ParametricInsurance | ERC-20 资金池;按批次投保,保单带有触发主题;当触发条件在到期前获得证明时,理赔即获赔付 |
编写自己的钩子
示例 contracts/examples/SeasonHook.sol 留在仓库中。test/standard/ImplementationGuide.test.js 为已获访问的读者演练它。SeasonalHook 是按商品和按操作设置多个窗口的参考模块。
经验法则:
- 符号
OK和OP_FORCED_TRANSFER在规范性文本中定义。 - 销毁行为在规范性文本中定义。
- 回调
check在规范性文本中定义。 - 诸如
ARexCodes.code("MYNS", "NAME")的代码名在规范性文本中定义。 - 标志值在规范性文本中定义。
- 回调
onExecuted和check在规范性文本中定义。
注册与排序
把成本低、拒绝率高的规则放在前面。HOOK_ADMIN_ROLE 应当置于多签或时间锁之后:谁控制管线,谁就控制哪些转移是可能的。
测试
test/standard/helpers.js 部署参考技术栈。RecordingHook 记录每一次回调,并且可以被指示以任意代码拒绝;ReentrantHook 尝试从 onExecuted 重新进入代币,以验证锁的有效性。DomainHooks.test.js 和 Extensions.test.js 覆盖移植而来的钩子和配套合约;Invariants.test.js 运行一个带种子的随机操作序列,并检查供应量守恒、终态,以及 canTransfer 能否预测每一种结果。
与 ERC-3643 合规的互操作
ERC-3643 合规模块公开 moduleCheck(from, to, value, compliance)。一个针对 OP_TRANSFER 调用它、并把 false 映射为某个代码的钩子,可以让为证券型代币编写的国家限制、余额上限和时间锁在批次代币上复用。反过来——把 A-REX 钩子暴露给 ERC-3643 代币——同样是机械性的。Chainlink 的 Automated Compliance Engine 通过同样的模式集成:其策略引擎成为一个钩子。