白皮书 · 16
附录
权威来源:contracts/standard/ .sol(CC0)和 standard/AREX CORE.md。接口 id 可由 npm run interface ids 重现。
附录 A:接口参考
权威来源:contracts/standard/*.sol(CC0)和 standard/AREX-CORE.md。接口 id 可由 npm run interface-ids 重现。
A.1 一致性概览
| 接口 | ERC-165 id | 代币 | 钩子 |
|---|---|---|---|
IERC1155 | 0xd9b67a26 | 必须 | — |
IERC7943MultiToken | 0x41c4fbad | 必须 | — |
IARexBatch | 0xe22639e0 | 必须 | — |
IARexHookManager | 0x964f4861 | 必须(getHooks) | — |
IARexAttest | 0xad603a6c | 应当(代币或配套合约) | — |
IARexHook | 0x332afcbb | — | 必须 |
A.2 IARexBatch
struct BatchInit { bytes32 commodity; bytes32 unit; uint64 producedAt; uint64 expiresAt; bytes32 originRef; bytes32 metadataHash; string uri; }
struct Batch { address issuer; uint64 createdAt; uint8 status; bytes32 commodity; bytes32 unit; uint64 producedAt; uint64 expiresAt; bytes32 originRef; bytes32 metadataHash; string uri; }
event BatchCreated(uint256 indexed id, address indexed issuer, bytes32 indexed commodity, bytes32 unit, uint256 quantity, address to);
event BatchStatusChanged(uint256 indexed id, uint8 previousStatus, uint8 newStatus, bytes32 reason);
event BatchMetadataUpdated(uint256 indexed id, bytes32 metadataHash, string uri);
event BatchTransformed(address indexed operator, uint256[] inputIds, uint256[] inputAmounts, uint256[] outputIds, uint256[] outputAmounts, bytes32 processRef);
error ARexBatchNotFound(uint256 id);
error ARexBatchRetired(uint256 id);
error ARexInvalidBatch(bytes32 reason);
error ARexInvalidStatusTransition(uint256 id, uint8 from, uint8 to);
error ARexLengthMismatch();
function createBatch(BatchInit calldata init, uint256 quantity, address to, bytes calldata data) external returns (uint256 id);
function batchOf(uint256 id) external view returns (Batch memory);
function batchExists(uint256 id) external view returns (bool);
function totalSupply(uint256 id) external view returns (uint256);
function setBatchStatus(uint256 id, uint8 status, bytes32 reason) external;
function setBatchMetadata(uint256 id, bytes32 metadataHash, string calldata uri) external;
function transform(address from, address to, uint256[] calldata inputIds, uint256[] calldata inputAmounts, BatchInit[] calldata outputs, uint256[] calldata outputAmounts, bytes32 processRef) external returns (uint256[] memory outputIds);
function parentsOf(uint256 id) external view returns (uint256[] memory);
A.3 IERC7943MultiToken(ERC-7943,原样)
event ForcedTransfer(address indexed from, address indexed to, uint256 indexed tokenId, uint256 amount);
event Frozen(address indexed account, uint256 indexed tokenId, uint256 amount);
error ERC7943CannotSend(address account);
error ERC7943CannotReceive(address account);
error ERC7943CannotTransfer(address from, address to, uint256 tokenId, uint256 amount);
error ERC7943InsufficientUnfrozenBalance(address account, uint256 tokenId, uint256 amount, uint256 unfrozen);
function forcedTransfer(address from, address to, uint256 tokenId, uint256 amount) external returns (bool result);
function setFrozenTokens(address account, uint256 tokenId, uint256 amount) external returns (bool result);
function canSend(address account) external view returns (bool allowed);
function canReceive(address account) external view returns (bool allowed);
function getFrozenTokens(address account, uint256 tokenId) external view returns (uint256 amount);
function canTransfer(address from, address to, uint256 tokenId, uint256 amount) external view returns (bool allowed);
A.4 IARexHook 与 IARexHookManager
interface IARexHook is IERC165 {
error ARexHookRejected(address hook, bytes32 code);
function hookFlags() external view returns (uint8);
function check(uint8 op, address operator, address from, address to, uint256 id, uint256 amount, bytes calldata data) external view returns (bytes32 code);
function onExecuted(uint8 op, address operator, address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
function canSend(address account) external view returns (bool);
function canReceive(address account) external view returns (bool);
}
interface IARexHookManager {
event HookRegistered(address indexed hook, uint8 flags, uint256 position);
event HookRemoved(address indexed hook);
error ARexHookAlreadyRegistered(address hook);
error ARexHookNotRegistered(address hook);
error ARexInvalidHook(address hook);
function getHooks() external view returns (address[] memory);
function hookFlagsOf(address hook) external view returns (uint8);
function registerHook(address hook) external;
function removeHook(address hook) external;
}
A.5 IARexAttest
struct Attestation { address issuer; bytes32 ref; uint64 issuedAt; uint64 expiresAt; bool revoked; }
event Attested(uint256 indexed id, bytes32 indexed topic, address indexed issuer, bytes32 ref, uint64 expiresAt);
event AttestationRevoked(uint256 indexed id, bytes32 indexed topic, address indexed issuer, bytes32 reason);
event TrustedIssuerUpdated(bytes32 indexed topic, address indexed issuer, bool trusted);
error ARexUntrustedIssuer(bytes32 topic, address issuer);
error ARexAttestationNotFound(uint256 id, bytes32 topic);
function attest(uint256 id, bytes32 topic, bytes32 ref, uint64 expiresAt) external;
function revokeAttestation(uint256 id, bytes32 topic, bytes32 reason) external;
function getAttestation(uint256 id, bytes32 topic) external view returns (Attestation memory);
function isAttested(uint256 id, bytes32 topic) external view returns (bool);
function isTrustedIssuer(bytes32 topic, address issuer) external view returns (bool);
A.6 常量(ARexCodes)
| 分组 | 取值 |
|---|---|
| 操作 | OP_MINT = 1、OP_TRANSFER = 2、OP_BURN = 3、OP_FORCED_TRANSFER = 4、OP_TRANSFORM = 5 |
| 钩子标志 | HOOK_CHECK = 1、HOOK_EXECUTED = 2、HOOK_ELIGIBILITY = 4 |
| 状态 | STATUS_ACTIVE = 0、STATUS_RETIRED = 255 |
代码(keccak256("AREX:…")) | PAUSED、FROZEN、SENDER_INELIGIBLE、RECEIVER_INELIGIBLE、SANCTIONED、BATCH_NOT_FOUND、BATCH_RETIRED、BATCH_INACTIVE、BATCH_EXPIRED、BATCH_NOT_PRODUCED、LIMIT_EXCEEDED、DOCUMENT_MISSING、SEASON_CLOSED、IN_TRANSIT、ATTESTATION_MISSING |
| 辅助函数 | commodity(scheme, code)、unit(code)、topic(name)、code(namespace, name) |
A.7 参考实现的角色
ARexToken 上的 ISSUER_ROLE、TRANSFORMER_ROLE、STATUS_ROLE、ENFORCER_ROLE、HOOK_ADMIN_ROLE、PAUSER_ROLE、UPGRADER_ROLE、DEFAULT_ADMIN_ROLE(每个均为 keccak256("AREX_<NAME>"));ARexAttestations 上的 ATTEST_ADMIN_ROLE。额外的视图函数:isExpired(id)、transferCheck(from, to, id, amount);额外的函数:burn(from, id, amount);ERC-7201 槽位 0x8ed599b6be05dec05297c009a1279a71df771ad996fe924d0b5a47326ff60900。
A.8 参考钩子与扩展
| 合约 | 类型 | 主要函数 |
|---|---|---|
PerishabilityHook | 钩子 | setCommodityDecay、setBatchDecay、valueBps、secondsRemaining、isExpired |
SeasonalHook | 钩子 | addWindow、clearWindows、windowsOf、isOpen |
DeliveryHook | 钩子 | schedule、pickUp、markInTransit、recordReading、confirmDelivered、cancel、dispute、resolve、getDelivery、lockedUnits |
TraceabilityHook | 钩子 | recordEvent、eventCount、getEvents;构造函数标志 persist |
CommodityPriceOracle | 扩展 | submit、finalizeRound、latestAnswer、getRound、currentRound、isStale、setParameters |
CarbonImmobilizationWrapper | 扩展 | recordImmobilization、linkToBatch、retire、release、attestBacking、backingOf、linksOf |
ParametricInsurance | 扩展 | fund、issue、claim、expire、getPolicy、freeCapacity |
A.9 元数据模式
standard/schema/batch-metadata.schema.json(JSON Schema 2020-12)。必需字段:schemaVersion、commodity{scheme, code}、unit、quantity。值得注意的可选字段组:traceabilityLotCode、gs1{gtin, lgtin, sscc, glnProducer, digitalLink}、production、origin{country, originRef, eudr{ddsReference}, geohashPrecision, geohash}、quality{gradeScheme, grade, measurements[]}、status.table、attestations[]、documents[]{type, hash, uri, registry, registryId}、epcis{bizStep, disposition, readPoint, bizLocation}、restricted{uri, hash, accessPolicy}、attributes[]。示例见 standard/examples/batch-metadata.example.json。
附录 B:术语表
| 术语 | 含义 |
|---|---|
| 批次 | 带有 Batch 记录的 ERC-1155 代币 id:同一种商品、同一单位、具有相同来源和生产时间的一批货物。 |
| 重新分批 | 输出与输入商品和单位相同、数量守恒的 transform(拆分、合并)。在参考实现中对持有人开放。 |
| 加工 | 改变商品、单位或数量的 transform。在参考实现中需要 TRANSFORMER_ROLE。 |
| 谱系 | 由 BatchTransformed 事件和 parentsOf 构成的图。 |
| 钩子 | 实现 IARexHook、注册在代币上、并在每次操作时按顺序求值的合约。 |
| 拒绝代码 | 钩子的 check 返回的 bytes32;0 表示允许;约定为 keccak256("NAMESPACE:NAME")。 |
| 资格 | 账户层面的发送或接收许可,由标记为 HOOK_ELIGIBILITY 的钩子回答;以 ERC-7943 canSend/canReceive 的形式公开。 |
| 冻结余额 | 账户持有但不能移动的单位(setFrozenTokens);可以超过余额。 |
| 强制转移 | ENFORCER_ROLE 在未经持有人同意的情况下移动单位(ERC-7943)。 |
| 证明 | 由该主题的可信签发方记录的 (batch, topic) → (issuer, reference, validity)。 |
| 主题 | 标识证明所涉事项的 bytes32;开放;可映射到 ERC-735 声明主题和 EAS schema id。 |
| 来源引用 | originRef:指向来源证据的哈希或引用;绝不是坐标。 |
| 元数据承诺 | metadataHash,即位于 uri 的规范链下文档的 keccak256。 |
| 状态 | uint8 生命周期取值;0 为活跃,255 为已注销(终态);1..254 由签发方定义。 |
| CTE / KDE | 关键追踪事件 / 关键数据元素(FSMA 204)。 |
| DDS | 尽职调查声明(EUDR)。 |
| EPCIS | GS1 Electronic Product Code Information Services,可追溯性的事件词汇表(2.0 版)。 |
| CPR / CPR-e | Cédula de Produto Rural,巴西的作物应收账款;其电子形式在获授权的实体处登记。 |
| EWR | 美国《仓储法》下的电子仓单。 |
| MLETR | UNCITRAL《电子可转让记录示范法》。 |
| ERC-7943 (uRWA) | 通用现实世界资产接口(Universal Real World Asset):冻结、强制转移、资格;Final,2026 年五月。 |
| ERC-3643 (T-REX) | 面向 ERC-20 上证券的许可制代币标准;Final,2023 年十二月。 |
| ERC-7201 | 用于可升级合约的命名空间存储布局。 |
附录 C:参考文献
标准与规范
- Radomski, W. 等。“EIP-1155: Multi Token Standard.” Ethereum Improvement Proposals,2018 年。
- Vogelsteller, F., Buterin, V. “EIP-20: Token Standard.” 2015 年。
- “ERC-165: Standard Interface Detection.” 2018 年。
- “ERC-3643: T-REX — Token for Regulated EXchanges.” Final,2023 年十二月 15 日。https://eips.ethereum.org/EIPS/eip-3643
- “ERC-7943: uRWA — Universal Real World Asset Interface.” Final,2026 年五月 27 日。https://eips.ethereum.org/EIPS/eip-7943
- “ERC-7201: Namespaced Storage Layout.” 2023 年。
- “ERC-735: Claim Holder.” 2017 年(草案)。
- Ethereum Attestation Service 文档。
- GS1. “EPCIS and CBV Standard 2.0.” 2022 年。GS1 US. “EPCIS Recommendations for FSMA 204 Critical Tracking Events.” 2024 年。
- UN/CEFACT. “Recommendation No. 20: Codes for Units of Measure Used in International Trade.”
- IETF RFC 8785. “JSON Canonicalization Scheme (JCS).” 2020 年。
- OpenZeppelin Contracts 5.x 文档;OpenZeppelin 与 T-REX Network 合作公告,2026 年七月。
监管与法律
- FDA. “Requirements for Additional Traceability Records for Certain Foods.” 最终规则,联邦公报 87(219),2022 年;“Compliance Date Extension,” 联邦公报,2025 年八月 7 日。
- 条例 (EU) 2023/1115(毁林);条例 (EU) 2025/2650(修订);欧盟理事会新闻稿,2025 年十二月 18 日。
- 条例 (EU) 2023/1114(MiCA);ESMA《关于将加密资产界定为金融工具的指南》,2024 年十二月。
- 欧洲数据保护委员会。“Guidelines 02/2025 on processing of personal data through blockchain technologies,” 2.0 版,2026 年七月 7 日。
- SEC。公告编号 33-11412,2026 年三月 17 日。SEC v. W.J. Howey Co.,328 U.S. 293 (1946)。
- GENIUS Act,公法 119-27(2025);实施条例,联邦公报,2026 年八月 18 日。
- 《统一商法典》2022 年修订,第 12 编(可控电子记录)。
- 美国《仓储法》;7 CFR Part 869(电子仓单)。
- UNCITRAL. “Model Law on Electronic Transferable Records.” 2017 年。英国,《2023 年电子贸易单据法》。
- Comisión Nacional de Valores(阿根廷)。Resolución General 1150/2026。Ley 9.643(权证)。
- 巴西。Lei 8.929/1994、Lei 13.986/2020(CPR);Lei 11.076/2004(CDA/WA);CVM Resolução 88 与 Consulta Pública SDM 05/2025。
- Verra. “Verra Addresses Crypto Instruments and Tokens.” 2022 年;VCS Program Guide。
- 指令 (EU) 2024/825(赋能消费者实现绿色转型)。
研究与行业
- Zhang, P., Schmidt, D. C. “Blockchain Applications in Food Supply Chain Management: A Systematic Review.” Journal of Food Engineering,279,2020 年。
- Caro, M. P. 等。“Blockchain-based Traceability in Agri-Food Supply Chain Management.” IEEE,2018 年。
- FAO. “The State of Food and Agriculture.” 2023 年。
- World Bank. “Future of Food: Harnessing Digital Technologies to Improve Food System Outcomes.” 2022 年。
- ERC3643 Association. “History of ERC-3643”;ISO TC 307 标准化倡议公告。
- Chainlink. “Automated Compliance Engine (ACE): Technical Overview.” 2025–2026 年。
附录 D:模板
D.1 最小钩子
来源:contracts/examples/MyHook.sol(导入 ../reference/hooks/ARexHookBase.sol 和 ../standard/IARexBatch.sol)。
contract MyHook is ARexHookBase {
function hookFlags() public pure override returns (uint8) {
return ARexCodes.HOOK_CHECK;
}
function check(uint8 op, address, address, address, uint256 id, uint256, bytes calldata)
external
view
override
returns (bytes32)
{
if (op != ARexCodes.OP_TRANSFER) return ARexCodes.OK;
IARexBatch.Batch memory b = IARexBatch(msg.sender).batchOf(id);
b; // decide from b.commodity, b.producedAt, b.expiresAt, b.status ...
return ARexCodes.OK;
}
}
D.2 部署(升级插件)
const token = await upgrades.deployProxy(await ethers.getContractFactory("ARexToken"), [admin, baseURI], { kind: "uups" });
const attestations = await (await ethers.getContractFactory("ARexAttestations")).deploy(admin, await token.getAddress());
await token.registerHook(await (await ethers.getContractFactory("EligibilityHook")).deploy(admin));
await token.registerHook(await (await ethers.getContractFactory("ExpiryHook")).deploy(true));
D.3 元数据骨架
{ "schemaVersion": "1.0.0", "commodity": { "scheme": "HS", "code": "100199" }, "unit": "KGM", "quantity": 250000,
"traceabilityLotCode": "…", "production": { "producedAt": "…" }, "origin": { "country": "AR", "originRef": "…" },
"status": { "table": { "1": "IN_STORAGE" } }, "documents": [ { "type": "warehouse_receipt", "hash": "…", "registryId": "…" } ],
"restricted": { "uri": "…", "hash": "…", "accessPolicy": "…" } }
附录 E:常见问题
A-REX 是 ERC 吗? 尚不是。该规范以 ERC 格式撰写,将在完成生产部署、出现独立实现并通过审计之后提交。尚未分配编号;代码中的名称不使用 ERC 前缀。
我必须使用 ARexToken 吗? 不必。可以用任何方式实现这些接口;参考实现只是其中一种方式。
我必须使用参考钩子吗? 不必。它们只是演示。可以复制、替换,或自行编写。
钩子可以阻止强制转移吗? 它可以针对 OP_FORCED_TRANSFER 返回一个代码,代币随即回滚。参考钩子不会这样做;规范强制执行的钩子(例如要求带有命令引用)则可以。
坐标放在哪里? 放在由元数据引用的受限链下存储中,绝不放在链上。
如何表示未来的收成? producedAt = 0;用 ExpiryHook(allowPreHarvestTransfers) 为转移设门槛。
transform 中的数量是否守恒? 标准并不要求;参考实现在数量守恒时允许任何持有人重新分批,否则需要 TRANSFORMER_ROLE。
代币是证券吗? 标准不作决定,发售才决定。请咨询法律顾问;无论答案如何,强制执行接口都在那里。