Whitepaper · 04
Solution Overview
Architecture of A-REX Core: three tiers with one direction of dependency, the transfer pipeline, the actors, and how the pieces are deployed.
Three tiers, one direction of dependency
A-REX separates what must be standardized from what must merely work:
The four layers of the standard
flowchart TB
L3["Layer 3 · IARexAttest<br/>attestations for a batch and a topic"]
L2["Layer 2 · IARexHook + IARexHookManager<br/>one ordered pipeline of rules"]
L1["Layer 1 · IARexBatch<br/>identity, status, a metadata commitment, and lineage"]
L0["Layer 0 · ERC-1155 + ERC-165 + IERC7943MultiToken<br/>balances and an enforcement surface"]
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 l0The two tiers of the repository and the direction of dependency
flowchart LR
STD["<b>The standard</b><br/>standard/ · contracts/standard/<br/>normative · CC0"]
REF["<b>Reference implementation</b><br/>ARexToken · ARexAttestations<br/>MIT"]
DEMO["<b>Demonstrations</b><br/>7 hooks · 3 extensions<br/>MIT · not normative"]
STD -->|implemented by| REF -->|demonstrated by| DEMO
DEMO -.->|depend only on the interfaces| STDThe standard never depends on the reference; the token never depends on the hooks. Remove every hook and every extension and a conformant token still exists. A third party obtains the normative text through early access, rather than from this public chapter.
What a batch is
A batch is an ERC-1155 token id. The field layout stays in the normative text.
Everything richer — grade, moisture, protein, packaging, GS1 identifiers, the traceability lot code, the EUDR declaration reference, lab reports, the issuer's status table — lives in the metadata document whose hash is committed on-chain. The document follows a published JSON Schema aligned with EPCIS 2.0 and the FSMA 204 key data elements.
The transfer pipeline
Every balance change — mint, transfer, burn, forced transfer, transformation — passes through one choke point in the reference token. The order is fixed and auditable:
flowchart TD
A[safeTransferFrom / createBatch / burn / forcedTransfer / transform] --> B[reentrancy lock]
B --> C{paused?}
C -- yes, not forced --> X1[EnforcedPause]
C -- no --> D{canSend from / canReceive to<br/>conjunction of ELIGIBILITY hooks}
D -- false --> X2[ERC7943CannotSend / CannotReceive]
D -- true --> E{per id: exists? not retired?<br/>amount ≤ balance − frozen?}
E -- fail --> X3[ARexBatchNotFound / ARexBatchRetired /<br/>ERC7943InsufficientUnfrozenBalance]
E -- ok --> F{per id, per CHECK hook in order:<br/>check → code}
F -- code ≠ 0 --> X4["ARexHookRejected(hook, code)"]
F -- all 0 --> G[balances updated · supply tracked]
G --> H[per id, per EXECUTED hook: onExecuted]
H --> I[unlock]Two views replay the same logic without changing state: canTransfer (boolean, as ERC-7943 requires) and transferCheck (the first rejection code, so an interface can tell the user why).
Forced transfers skip the pause and the eligibility checks — enforcement must work when everything else is frozen — but still run the hooks with OP_FORCED_TRANSFER, so a hook can insist, for example, on a court-order reference in the transfer data.
Lineage
transform is the single operation for splitting, merging and processing:
graph LR
W1["wheat #1<br/>250 t · farmer"] -- "transform (re-batch in place)" --> W2["wheat #2<br/>100 t · farmer"]
W1 -- "transform (re-batch in place)" --> W3["wheat #3<br/>150 t · farmer"]
W2 -- "transfer" --> M2["wheat #2<br/>100 t · mill"]
M2 -- "transform (processing, TRANSFORMER_ROLE)" --> F4["flour #4<br/>75 t · mill"]- Re-batching (same commodity, same unit, conserved quantity) is open to any holder or approved operator as long as the outputs stay with the holder (
to == from): a cooperative splitting a silo lot before allocating it to members, an exporter merging deliveries it has received. The new lots reach another holder through an ordinary transfer, which runs every transfer check and hook; re-batching straight into another account requiresTRANSFORMER_ROLE. - Processing (anything else) additionally requires
TRANSFORMER_ROLE. parentsOf(4) == [2],parentsOf(2) == [1]; oneBatchTransformedevent per operation, which is an EPCISTransformationEventby construction.
Enforcement
ENFORCER_ROLE can freeze any amount for any account (setFrozenTokens, may exceed the balance, as ERC-7943 specifies) and move tokens without the holder's consent (forcedTransfer). This is what makes a batch token usable outside a demo: a lender executing a warrant, a court ordering the seizure of stored grain, a warehouse recovering a lot after a customer loses keys. Every enforcement action is an event.
Hooks
A hook is a contract with five functions and a constant flags bitmap. The token caches the flags at registration, so a transfer costs one external call per interested hook, not one interface probe per capability per module. A hook learns which token is calling from msg.sender; one deployment can serve many tokens.
Reference hooks demonstrate the three capabilities:
| Hook | Capability | What it proves |
|---|---|---|
EligibilityHook | CHECK + ELIGIBILITY | allow/block lists and sanctions feed canSend / canReceive and produce precise codes |
ExpiryHook | CHECK | perishability and pre-harvest gating derived purely from the batch record |
AttestationHook | CHECK | "coffee needs an EUDR declaration attested before it can be sold" |
PerishabilityHook | CHECK | decay models and value views; expired or valueless lots do not move |
SeasonalHook | CHECK | harvest and trading windows per commodity |
DeliveryHook | CHECK + EXECUTED | custody lock during delivery that transfers, burns and re-lotting cannot get around; settlement is the transfer itself, identified by data |
TraceabilityHook | EXECUTED | EPCIS-shaped event log of every operation plus manual critical tracking events |
Companion extensions (not hooks): CommodityPriceOracle, CarbonImmobilizationWrapper, ParametricInsurance.
Attestations
A companion contract records (batch, topic) → (issuer, reference, validity). Only issuers trusted for a topic can attest; revocation, expiry and loss of trust all make isAttested false. Topics are bytes32, so ERC-735 claim topics, Ethereum Attestation Service schema ids and hashes of certification names all work without a table in the standard.
Actors and roles
| Actor | Typical role in the reference token | Typical action |
|---|---|---|
| Originator platform, cooperative, elevator | ISSUER_ROLE | createBatch |
| Farmer / holder | none | transfer, re-batch in place, burn |
| Mill, packer, processor | TRANSFORMER_ROLE | processing transform |
| Warehouse, inspector, platform | STATUS_ROLE | setBatchStatus, setBatchMetadata |
| Certifier, laboratory, authority | trusted issuer for a topic | attest |
| Lender, court-appointed custodian, compliance | ENFORCER_ROLE | setFrozenTokens, forcedTransfer |
| Governance (multisig, timelock) | DEFAULT_ADMIN_ROLE, HOOK_ADMIN_ROLE, UPGRADER_ROLE, PAUSER_ROLE | roles, hooks, upgrades, pause |
Nothing in the standard fixes these roles; they are how the reference implementation exercises the MUSTs ("restricted to an authorized role").
Deployment shapes
- Public L2 (Polygon, Arbitrum, Base, Optimism): cheapest, most composable; suited to commodity trading and financing where counterparties are businesses.
- Permissioned EVM chain: where data-protection authorities or the anchor institution require it; the EDPB expressly encourages permissioned designs. The standard is chain-agnostic.
- Hybrid: batches on a public chain, restricted data (plot polygons, producer identity) behind access-controlled storage referenced from metadata.
The reference stack deploys with one Ignition module or one script: token proxy, attestation registry, eligibility and expiry hooks.
Design decisions and the alternatives they replace
| Alternative considered | A-REX Core | Reason |
|---|---|---|
| a wide core interface with enums for commodity, grade, state | 8-function IARexBatch with open codes | nothing to redeploy for a new crop, scheme or lifecycle |
| capability interfaces probed on every transfer | one hook interface, flags cached | gas linear in hooks, not in ids × modules × probes |
| string reasons | bytes32 codes and typed errors | machine-readable, indexable |
| lifecycle state changeable by any holder | role only; RETIRED terminal | security |
| harvest date required in the past | producedAt == 0 allowed | pre-harvest financing |
| no split / merge / processing | transform + parentsOf | traceability, recalls, EPCIS |
| no freeze or forced transfer | ERC-7943 | enforceability |
| coordinates stored on-chain | origin reference only | GDPR / EDPB 02/2025 |
| registry and identity contracts required | nothing required | no imposed identity stack |