白皮书 · 04
解决方案概述
A-REX Core 的架构:依赖方向单一的三个层级、转移管线、参与者,以及各部分如何部署。
三个层级,一个依赖方向
A-REX 把必须被标准化的部分与仅需可用的部分分开:
标准的四个层次
flowchart TB
L3["第 3 层 · IARexAttest<br/>attest · revokeAttestation · isAttested · 按主题的可信签发方"]
L2["第 2 层 · IARexHook + IARexHookManager<br/>hookFlags · check → bytes32 · onExecuted · getHooks"]
L1["第 1 层 · IARexBatch<br/>createBatch · batchOf · setBatchStatus · setBatchMetadata · transform · parentsOf"]
L0["第 0 层 · ERC-1155 + ERC-165 + IERC7943MultiToken<br/>冻结 · forcedTransfer · canSend · canReceive · canTransfer"]
L3 --- L2 --- L1 --- L0
classDef l3 fill:#edf5ff,stroke:#0f62fe,color:#161616
classDef l2 fill:#d0e2ff,stroke:#0f62fe,color:#161616
classDef l1 fill:#a6c8ff,stroke:#0f62fe,color:#161616
classDef l0 fill:#78a9ff,stroke:#0043ce,color:#161616
class L3 l3
class L2 l2
class L1 l1
class L0 l0代码仓库的两个层级与依赖方向
flowchart LR
STD["<b>标准</b><br/>standard/ · contracts/standard/<br/>规范性 · CC0"]
REF["<b>参考实现</b><br/>ARexToken · ARexAttestations<br/>MIT"]
DEMO["<b>演示</b><br/>7 个钩子 · 3 个扩展<br/>MIT · 非规范性"]
STD -->|被实现| REF -->|被演示| DEMO
DEMO -.->|仅依赖接口| STD标准从不依赖参考实现;代币从不依赖钩子。移除每一个钩子和每一个扩展之后,仍然存在一个符合一致性要求的代币。正是这一性质,使第三方可以实现 A-REX,而除了六个接口文件之外无需采用本仓库中的任何东西。
批次是什么
批次是一个带有记录的 ERC-1155 代币 id:
struct Batch {
address issuer; // who created it (or performed the transformation)
uint64 createdAt;
uint8 status; // 0 ACTIVE · 255 RETIRED (terminal) · 1..254 issuer-defined
bytes32 commodity; // keccak256("HS:100199"), keccak256("FAO:0111"), keccak256("GTIN:0779…")
bytes32 unit; // UN/CEFACT Rec. 20: "KGM", "TNE", "LTR", "BX"
uint64 producedAt; // 0 = not yet produced (pre-harvest)
uint64 expiresAt; // 0 = none
bytes32 originRef; // hash / reference to origin evidence — never coordinates
bytes32 metadataHash; // keccak256 of the off-chain document
string uri; // where the document is
}
所有更丰富的内容——等级、水分、蛋白质、包装、GS1 标识符、可追溯性批次代码、EUDR 声明引用、实验室报告、签发方的状态表——都存放在元数据文档中,该文档的哈希已承诺上链。该文档遵循一份已发布的、与 EPCIS 2.0 和 FSMA 204 关键数据元素对齐的 JSON Schema。
转移管线
每一次余额变动——铸造、转移、销毁、强制转移、转化——在参考代币中都要经过同一个必经关口。其顺序是固定且可审计的:
flowchart TD
A[safeTransferFrom / createBatch / burn / forcedTransfer / transform] --> B["重入锁"]
B --> C{"是否已暂停?"}
C -- 是,且非强制 --> X1[EnforcedPause]
C -- 否 --> D{"canSend from / canReceive to<br/>所有 ELIGIBILITY 钩子的合取"}
D -- 否 --> X2[ERC7943CannotSend / CannotReceive]
D -- 是 --> E{"逐个 id:是否存在?未注销?<br/>数量 ≤ 余额 − 冻结量?"}
E -- 未通过 --> X3[ARexBatchNotFound / ARexBatchRetired /<br/>ERC7943InsufficientUnfrozenBalance]
E -- 通过 --> F{"逐个 id,按顺序逐个 CHECK 钩子:<br/>check → 代码"}
F -- 代码 ≠ 0 --> X4["ARexHookRejected(hook, code)"]
F -- 全部为 0 --> G["余额已更新 · 已记录供应量"]
G --> H["逐个 id,逐个 EXECUTED 钩子:onExecuted"]
H --> I["解锁"]有两个视图函数会在不改变状态的情况下重放同一套逻辑:canTransfer(布尔值,如 ERC-7943 所要求)和 transferCheck(第一个拒绝代码,使界面能够告诉用户为什么)。
强制转移会跳过暂停和资格检查——强制执行必须在其他一切都被冻结时仍能工作——但仍会以 OP_FORCED_TRANSFER 运行钩子,因此钩子可以要求,例如,转移数据中必须带有法院命令引用。
谱系
transform 是拆分、合并和加工的唯一操作:
graph LR
W1["小麦 #1<br/>250 吨 · 农户"] -- "transform(重新分批)" --> W2["小麦 #2<br/>100 吨 · 买方 A"]
W1 -- "transform(重新分批)" --> W3["小麦 #3<br/>150 吨 · 买方 B"]
W2 -- "transform(加工,TRANSFORMER_ROLE)" --> F4["面粉 #4<br/>75 吨 · 磨坊"]- 重新分批(商品相同、单位相同、数量守恒)对任何持有人或获批准的操作方开放:例如合作社在成员之间拆分一个筒仓批次,或出口商合并多批交付。
- 加工(其他任何情形)还需要
TRANSFORMER_ROLE。 parentsOf(4) == [2]、parentsOf(2) == [1];每次操作一个BatchTransformed事件,按其构造即是一个 EPCISTransformationEvent。
强制执行
ENFORCER_ROLE 可以为任何账户冻结任意数量(setFrozenTokens,可以超过余额,如 ERC-7943 所规定),并且可以在未经持有人同意的情况下移动代币(forcedTransfer)。正是这一点使批次代币在演示之外也可用:贷款方执行权证、法院下令查封仓储谷物、仓库在客户丢失密钥后收回批次。每一次强制执行操作都是一个事件。
钩子
钩子是一个包含五个函数和一个常量标志位图的合约。代币在注册时缓存这些标志,因此一次转移对每个相关钩子只产生一次外部调用,而不是对每个模块的每种能力各做一次接口探测。钩子通过 msg.sender 得知是哪个代币在调用;一次部署可以为多个代币服务。
参考钩子演示了这三种能力:
| 钩子 | 能力 | 证明了什么 |
|---|---|---|
EligibilityHook | CHECK + ELIGIBILITY | 白名单/黑名单和制裁名单为 canSend / canReceive 提供依据,并产生精确的代码 |
ExpiryHook | CHECK | 完全由批次记录推导出的易腐性和收获前设门槛 |
AttestationHook | CHECK | “咖啡必须先有经证明的 EUDR 声明,才能出售” |
PerishabilityHook | CHECK | 衰减模型和价值视图;已到期或已无价值的批次无法转移 |
SeasonalHook | CHECK | 按商品的收获窗口和交易窗口 |
DeliveryHook | CHECK + EXECUTED | 交付期间的保管锁定;结算就是转移本身,由 data 标识 |
TraceabilityHook | EXECUTED | 每次操作的 EPCIS 形态事件日志,外加手动录入的关键追踪事件 |
配套扩展(不是钩子):CommodityPriceOracle、CarbonImmobilizationWrapper、ParametricInsurance。
证明
一个配套合约记录 (batch, topic) → (issuer, reference, validity)。只有对某个主题受信任的签发方才能出具证明;撤销、到期和失去信任都会使 isAttested 返回 false。主题是 bytes32,因此 ERC-735 声明主题、Ethereum Attestation Service 的 schema id 以及认证名称的哈希都可以使用,而无需在标准中维护一张表。
参与者与角色
| 参与者 | 参考代币中的典型角色 | 典型操作 |
|---|---|---|
| 发起平台、合作社、粮库 | ISSUER_ROLE | createBatch |
| 农户 / 持有人 | 无 | 转移、重新分批、销毁 |
| 磨坊、包装商、加工商 | TRANSFORMER_ROLE | 加工类 transform |
| 仓库、检验员、平台 | STATUS_ROLE | setBatchStatus、setBatchMetadata |
| 认证机构、实验室、主管机关 | 某个主题的可信签发方 | attest |
| 贷款方、法院指定的保管人、合规部门 | ENFORCER_ROLE | setFrozenTokens、forcedTransfer |
| 治理(多签、时间锁) | DEFAULT_ADMIN_ROLE、HOOK_ADMIN_ROLE、UPGRADER_ROLE、PAUSER_ROLE | 角色、钩子、升级、暂停 |
标准中没有任何内容固定这些角色;它们是参考实现履行各项“必须”条款(“限于获授权角色”)的方式。
部署形态
- 公共 L2(Polygon、Arbitrum、Base、Optimism):成本最低、可组合性最强;适合交易对手为企业的商品交易与融资。
- 许可制 EVM 链:适用于数据保护机构或核心机构要求之处;EDPB 明确鼓励许可制设计。该标准与具体链无关。
- 混合式:批次在公共链上,受限数据(地块多边形、生产者身份)则放在受访问控制的存储之后,由元数据引用。
参考技术栈可通过一个 Ignition 模块或一个脚本部署:代币代理、证明注册表、资格钩子和到期钩子。
设计决策及其所取代的备选方案
| 曾考虑的备选方案 | A-REX Core | 理由 |
|---|---|---|
| 带有商品、等级、状态枚举的宽核心接口 | 含 8 个函数的 IARexBatch,使用开放代码 | 新增作物、方案或生命周期时无需重新部署任何东西 |
| 每次转移都要探测的能力接口 | 一个钩子接口,标志已缓存 | Gas 随钩子数量线性增长,而不是随 id × 模块 × 探测次数增长 |
| 字符串形式的原因 | bytes32 代码和类型化错误 | 机器可读、可索引 |
| 任何持有人都能更改的生命周期状态 | 仅限角色;RETIRED 为终态 | 安全 |
| 要求收获日期必须在过去 | 允许 producedAt == 0 | 收获前融资 |
| 无拆分 / 合并 / 加工 | transform + parentsOf | 可追溯性、召回、EPCIS |
| 无冻结或强制转移 | ERC-7943 | 可强制执行性 |
| 坐标存储在链上 | 仅存来源引用 | GDPR / EDPB 02/2025 |
| 要求有注册表和身份合约 | 无任何强制要求 | 不强加身份技术栈 |