标准
Interface reference
Every function, event, error and type of the five normative interfaces, generated from contracts/standard/ and its NatSpec on each build. For the normative prose, read the specification.
| Interface | Layer | ERC-165 id | Required | Source |
|---|---|---|---|---|
IERC7943MultiToken | 0 | 0x41c4fbad | MUST | contracts/standard/IERC7943MultiToken.sol |
IARexBatch | 1 | 0xe22639e0 | MUST | contracts/standard/IARexBatch.sol |
IARexHook | 2 | 0x332afcbb | MUST for hooks | contracts/standard/IARexHook.sol |
IARexHookManager | 2 | 0x964f4861 | MUST (getHooks) | contracts/standard/IARexHookManager.sol |
IARexAttest | 3 | 0xad603a6c | SHOULD (token or companion) | contracts/standard/IARexAttest.sol |
IERC7943MultiToken
interface IERC7943MultiToken is IERC165 · layer 0 · id 0x41c4fbad · MUST
ERC-1155 variant of ERC-7943 (uRWA, Universal Real World Asset interface).
Reproduced verbatim from the Final ERC-7943 text so that A-REX tokens are detectable by any ERC-7943-aware integrator. Interface ID: 0x41c4fbad. A-REX requires every conformant token to implement this interface; it supplies the enforcement primitives (freeze, forced transfer, eligibility) that regulators, custodians and courts expect from a token backed by a physical asset.
Functions
forcedTransfer
function forcedTransfer(address from, address to, uint256 tokenId, uint256 amount) external returns (bool result);
Takes tokens from one address and transfers them to another.
Requires specific authorization. Used for regulatory compliance or recovery scenarios.
setFrozenTokens
function setFrozenTokens(address account, uint256 tokenId, uint256 amount) external returns (bool result);
Changes the frozen status of amount of tokenId tokens belonging to an account.
Overwrites the current value, similar to an approve function. It can exceed the balance.
canSend
function canSend(address account) external view returns (bool allowed);
Checks if a specific account is allowed to send tokens according to token rules.
canReceive
function canReceive(address account) external view returns (bool allowed);
Checks if a specific account is allowed to receive tokens according to token rules.
getFrozenTokens
function getFrozenTokens(address account, uint256 tokenId) external view returns (uint256 amount);
Checks the frozen amount of a specific tokenId for account.
canTransfer
function canTransfer(address from, address to, uint256 tokenId, uint256 amount) external view returns (bool allowed);
Checks if a transfer is currently possible according to token rules, including frozen balances.
Events
| Event | When |
|---|---|
ForcedTransfer(address indexed from, address indexed to, uint256 indexed tokenId, uint256 amount) | Emitted when tokens are taken from one address and transferred to another. |
Frozen(address indexed account, uint256 indexed tokenId, uint256 amount) | Emitted when setFrozenTokens is called, changing the frozen amount of tokenId tokens for account. |
Errors
| Error | Raised when |
|---|---|
ERC7943CannotSend(address account) | Error reverted when an account is not allowed to send tokens. |
ERC7943CannotReceive(address account) | Error reverted when an account is not allowed to receive tokens. |
ERC7943CannotTransfer(address from, address to, uint256 tokenId, uint256 amount) | Error reverted when a transfer is not allowed according to internal rules. |
ERC7943InsufficientUnfrozenBalance(address account, uint256 tokenId, uint256 amount, uint256 unfrozen) | Error reverted when a transfer is attempted from account with an amount of tokenId less than or equal to its balance, but greater than its unfrozen balance. |
IARexBatch
interface IARexBatch is IERC165 · layer 1 · id 0xe22639e0 · MUST
A-REX Core, layer 1: the minimal on-chain representation of an agricultural batch.
This interface is deliberately small. Everything agricultural that is not the
identity of a batch or its lineage belongs in hooks (IARexHook) or in off-chain
metadata referenced by uri and committed by metadataHash.
Design rules (normative, see standard/AREX-CORE.md):
- No closed enumerations. Commodity, unit and status are open codes so that a new crop, a new grading scheme or a new lifecycle can be adopted without redeploying.
- No personal data. Geolocation of a plot is personal data of the producer under the
GDPR and Latin American data-protection laws; only a reference (
originRef) is stored. - Pre-harvest is a first-class case:
producedAt == 0means "not yet produced". - Lineage is mandatory:
transformis the only way to split, merge or process a batch, so every output batch can be traced back to its inputs (EPCIS TransformationEvent). - Status is changed by an authorized role, never by holding a balance.
Interface ID: see ARexInterfaceIds.IAREX_BATCH.
struct BatchInit
Fields supplied by the issuer when a batch is created or transformed.
| Field | Type | Meaning |
|---|---|---|
commodity | bytes32 | Namespaced commodity code, e.g. ARexCodes.commodity("HS", "100199"). |
unit | bytes32 | UN/CEFACT Recommendation 20 unit code, e.g. bytes32("KGM"). |
producedAt | uint64 | Unix time of harvest/production; 0 = not yet produced (pre-harvest). |
expiresAt | uint64 | Unix time after which the batch is considered expired; 0 = none. |
originRef | bytes32 | Hash or reference to origin evidence (EUDR DDS reference, registry ID, truncated geohash). Never raw coordinates. |
metadataHash | bytes32 | keccak256 of the canonical off-chain metadata document. |
uri | string | Location of the metadata document (IPFS, HTTPS, GS1 Digital Link). |
struct Batch
Full batch record as stored by the token.
| Field | Type | Meaning |
|---|---|---|
issuer | address | Account that created the batch (or performed the transformation). |
createdAt | uint64 | Block timestamp of creation. |
status | uint8 | 0 = ACTIVE, 255 = RETIRED (terminal). 1..254 are issuer-defined. |
commodity | bytes32 | Namespaced commodity code, e.g. ARexCodes.commodity("HS", "100199"). |
unit | bytes32 | UN/CEFACT Recommendation 20 unit code, e.g. bytes32("KGM"). |
producedAt | uint64 | Unix time of harvest/production; 0 = not yet produced (pre-harvest). |
expiresAt | uint64 | Unix time after which the batch is considered expired; 0 = none. |
originRef | bytes32 | Hash or reference to origin evidence (EUDR DDS reference, registry ID, truncated geohash). Never raw coordinates. |
metadataHash | bytes32 | keccak256 of the canonical off-chain metadata document. |
uri | string | Location of the metadata document (IPFS, HTTPS, GS1 Digital Link). |
Functions
createBatch
function createBatch(BatchInit calldata init, uint256 quantity, address to, bytes calldata data) external returns (uint256 id);
Create a new batch and mint quantity units of it to to.
MUST revert if quantity == 0, if init.commodity == 0 or init.unit == 0,
or if init.expiresAt != 0 && init.producedAt != 0 && init.expiresAt <= init.producedAt.
MUST emit BatchCreated and the ERC-1155 TransferSingle event.
MUST run the hook pipeline with op = OP_MINT.
| Parameter | Meaning |
|---|---|
init | Batch fields. |
quantity | Units minted, expressed in init.unit. |
to | Recipient of the minted units. |
data | Forwarded to hooks and to the ERC-1155 receiver. |
Returns: id The new batch / token id.
batchOf
function batchOf(uint256 id) external view returns (Batch memory);
Returns the batch record. MUST revert with ARexBatchNotFound if id does not exist.
batchExists
function batchExists(uint256 id) external view returns (bool);
Non-reverting existence check for hooks and integrators.
totalSupply
function totalSupply(uint256 id) external view returns (uint256);
Circulating units of the batch (ERC-1155 supply tracking is mandatory).
setBatchStatus
function setBatchStatus(uint256 id, uint8 status, bytes32 reason) external;
Change the lifecycle status of a batch.
MUST be restricted to an authorized role; MUST NOT be granted by token balance.
Status 255 (RETIRED) is terminal: once set it MUST NOT change.
MUST emit BatchStatusChanged.
setBatchMetadata
function setBatchMetadata(uint256 id, bytes32 metadataHash, string calldata uri) external;
Update the metadata commitment of a batch (e.g. after a lab result is added).
MUST be restricted to an authorized role. MUST emit BatchMetadataUpdated.
transform
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);
Burn inputAmounts of inputIds held by from and mint new batches outputs
with outputAmounts to to, recording lineage.
Covers split (1 input, n outputs), merge (n inputs, 1 output) and processing (n → m).
msg.sender MUST be from or an approved operator of from (ERC-1155 semantics).
MUST revert with ARexLengthMismatch on inconsistent array lengths.
MUST run the hook pipeline with op = OP_TRANSFORM for every input burn and output mint.
MUST emit BatchTransformed, one BatchCreated per output, and ERC-1155 transfer events.
Conservation of quantity is NOT enforced by the standard (processing changes units and mass);
it is the job of hooks and off-chain audit.
Returns: outputIds Ids of the created output batches, in order.
parentsOf
function parentsOf(uint256 id) external view returns (uint256[] memory);
Direct parents of a batch (empty for batches created with createBatch).
Events
| Event | When |
|---|---|
BatchCreated(uint256 indexed id, address indexed issuer, bytes32 indexed commodity, bytes32 unit, uint256 quantity, address to) | Emitted once per batch creation, including outputs of transform. |
BatchStatusChanged(uint256 indexed id, uint8 previousStatus, uint8 newStatus, bytes32 reason) | Emitted when the lifecycle status of a batch changes. |
BatchMetadataUpdated(uint256 indexed id, bytes32 metadataHash, string uri) | Emitted when the metadata commitment of a batch is updated. |
BatchTransformed(address indexed operator, uint256[] inputIds, uint256[] inputAmounts, uint256[] outputIds, uint256[] outputAmounts, bytes32 processRef) | Emitted by transform. Maps one-to-one to an EPCIS 2.0 TransformationEvent. |
Errors
| Error | Raised when |
|---|---|
ARexBatchNotFound(uint256 id) | |
ARexBatchRetired(uint256 id) | |
ARexInvalidBatch(bytes32 reason) | |
ARexInvalidStatusTransition(uint256 id, uint8 from, uint8 to) | |
ARexLengthMismatch() |
IARexHook
interface IARexHook is IERC165 · layer 2 · id 0x332afcbb · MUST for hooks
A-REX Core, layer 2: the single extension point of a conformant token.
Every rule that is not the identity of a batch (eligibility, sanctions, expiry, seasonal windows, custody, transfer limits, documents, escrow, ...) is a hook. Hooks are contracts, deployed by anyone, attached to a token by its administrator.
The token calls hooks in registration order and stops at the first non-zero code.
A hook never reverts a transfer directly: it returns a bytes32 code and the token
reverts with ARexHookRejected(hook, code). Codes are open, namespaced constants
(see ARexCodes) so that front-ends, indexers and auditors can interpret them.
hookFlags() tells the token which callbacks the hook wants; the token caches the
flags at registration so that a transfer costs one external call per interested hook,
not one supportsInterface probe per capability.
The hook learns which token is calling from msg.sender; a single hook deployment
can therefore serve many tokens.
Functions
hookFlags
function hookFlags() external view returns (uint8);
Bitmap of callbacks this hook implements.
bit 0 (ARexCodes.HOOK_CHECK): check is called before every operation.
bit 1 (ARexCodes.HOOK_EXECUTED): onExecuted is called after every operation.
bit 2 (ARexCodes.HOOK_ELIGIBILITY): canSend / canReceive are consulted.
A conformant hook MUST return a constant value.
check
function check(uint8 op, address operator, address from, address to, uint256 id, uint256 amount, bytes calldata data) external view returns (bytes32 code);
Validate an operation. MUST be a view. MUST NOT revert for policy reasons;
return a non-zero code instead. bytes32(0) means allowed.
| Parameter | Meaning |
|---|---|
op | ARexCodes.OP_MINT / OP_TRANSFER / OP_BURN / OP_FORCED_TRANSFER / OP_TRANSFORM. |
operator | msg.sender of the token call that triggered the operation. |
from | Source account (address(0) for mints). |
to | Destination account (address(0) for burns). |
id | Batch id. |
amount | Units affected. |
data | Opaque data forwarded by the token. |
onExecuted
function onExecuted(uint8 op, address operator, address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
Notification after an operation has been applied to balances.
MUST NOT re-enter the token's balance-changing functions; the token guards against it.
Parameters as in check.
canSend
function canSend(address account) external view returns (bool);
Account-level eligibility to send (KYC/KYB, sanctions, allowlists). Only consulted if HOOK_ELIGIBILITY is set.
canReceive
function canReceive(address account) external view returns (bool);
Account-level eligibility to receive. Only consulted if HOOK_ELIGIBILITY is set.
Errors
| Error | Raised when |
|---|---|
ARexHookRejected(address hook, bytes32 code) | Reverted by the token when a hook returns a non-zero code. |
IARexHookManager
interface IARexHookManager · layer 2 · id 0x964f4861 · MUST (getHooks)
A-REX Core, layer 2 (token side): how a token exposes and manages its hook pipeline.
getHooks is mandatory so that anyone can audit which rules govern a token.
registerHook / removeHook are the recommended administrative surface; a token MAY
use a different governance mechanism (timelock, multisig, immutable list) as long as
the events below are emitted.
Functions
getHooks
function getHooks() external view returns (address[] memory);
Ordered list of active hooks. Hooks are evaluated in this order.
hookFlagsOf
function hookFlagsOf(address hook) external view returns (uint8);
Cached flags of a registered hook (0 if not registered).
registerHook
function registerHook(address hook) external;
Append a hook to the pipeline. MUST verify supportsInterface(IARexHook) and cache hookFlags().
removeHook
function removeHook(address hook) external;
Remove a hook, preserving the relative order of the remaining hooks.
Events
| Event | When |
|---|---|
HookRegistered(address indexed hook, uint8 flags, uint256 position) | |
HookRemoved(address indexed hook) |
Errors
| Error | Raised when |
|---|---|
ARexHookAlreadyRegistered(address hook) | |
ARexHookNotRegistered(address hook) | |
ARexInvalidHook(address hook) |
IARexAttest
interface IARexAttest is IERC165 · layer 3 · id 0xad603a6c · SHOULD (token or companion)
A-REX Core, layer 3: open attestations about a batch (certifications, inspections, lab results, regulatory declarations).
An attestation is (batch id, topic) -> (issuer, reference, validity window).
Topics are open bytes32 values, namespaced by convention (see ARexCodes.topic), so a
new certification scheme never requires a redeploy. They map directly to:
- ERC-735 / ONCHAINID claim topics (uint256 -> bytes32),
- Ethereum Attestation Service schema UIDs (bytes32),
- W3C Verifiable Credential types (hash of the type URI).
The token keeps only the commitment; the evidence lives off-chain at
ref. Only issuers trusted for a topic can attest to it, mirroring ERC-3643's TrustedIssuersRegistry without imposing an identity provider.
struct Attestation
| Field | Type | Meaning |
|---|---|---|
issuer | address | |
ref | bytes32 | Hash of the evidence document or external certificate id. |
issuedAt | uint64 | |
expiresAt | uint64 | 0 = no expiry. |
revoked | bool |
Functions
attest
function attest(uint256 id, bytes32 topic, bytes32 ref, uint64 expiresAt) external;
Record an attestation. msg.sender MUST be a trusted issuer for topic.
Re-attesting the same (id, topic) overwrites the record; history is in the event log.
revokeAttestation
function revokeAttestation(uint256 id, bytes32 topic, bytes32 reason) external;
Revoke an attestation. MUST be callable by its issuer; MAY be callable by an admin role.
getAttestation
function getAttestation(uint256 id, bytes32 topic) external view returns (Attestation memory);
Raw record. MUST revert with ARexAttestationNotFound if none exists.
isAttested
function isAttested(uint256 id, bytes32 topic) external view returns (bool);
True if an attestation exists, is not revoked, is not expired and its issuer is still trusted.
isTrustedIssuer
function isTrustedIssuer(bytes32 topic, address issuer) external view returns (bool);
Whether issuer may attest to topic.
Events
| Event | When |
|---|---|
Attested(uint256 indexed id, bytes32 indexed topic, address indexed issuer, bytes32 ref, uint64 expiresAt) | |
AttestationRevoked(uint256 indexed id, bytes32 indexed topic, address indexed issuer, bytes32 reason) | |
TrustedIssuerUpdated(bytes32 indexed topic, address indexed issuer, bool trusted) |
Errors
| Error | Raised when |
|---|---|
ARexUntrustedIssuer(bytes32 topic, address issuer) | |
ARexAttestationNotFound(uint256 id, bytes32 topic) |
ARexCodes
Constants shared by tokens, hooks and front-ends (contracts/standard/ARexCodes.sol). Only the operation, flag and status values are normative; the rejection codes are recommended.
| Group | Name | Type | Value |
|---|---|---|---|
| Operations (fixed by the standard) | OP_MINT | uint8 | 1 |
| Operations (fixed by the standard) | OP_TRANSFER | uint8 | 2 |
| Operations (fixed by the standard) | OP_BURN | uint8 | 3 |
| Operations (fixed by the standard) | OP_FORCED_TRANSFER | uint8 | 4 |
| Operations (fixed by the standard) | OP_TRANSFORM | uint8 | 5 |
| Hook flags (fixed by the standard) | HOOK_CHECK | uint8 | 1 << 0 |
| Hook flags (fixed by the standard) | HOOK_EXECUTED | uint8 | 1 << 1 |
| Hook flags (fixed by the standard) | HOOK_ELIGIBILITY | uint8 | 1 << 2 |
| Batch status (fixed by the standard) | STATUS_ACTIVE | uint8 | 0 |
| Batch status (fixed by the standard) | STATUS_RETIRED | uint8 | 255 |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | OK | bytes32 | bytes32(0) |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | PAUSED | bytes32 | keccak256("AREX:PAUSED") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | FROZEN | bytes32 | keccak256("AREX:FROZEN") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | SENDER_INELIGIBLE | bytes32 | keccak256("AREX:SENDER_INELIGIBLE") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | RECEIVER_INELIGIBLE | bytes32 | keccak256("AREX:RECEIVER_INELIGIBLE") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | SANCTIONED | bytes32 | keccak256("AREX:SANCTIONED") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | BATCH_NOT_FOUND | bytes32 | keccak256("AREX:BATCH_NOT_FOUND") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | BATCH_RETIRED | bytes32 | keccak256("AREX:BATCH_RETIRED") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | BATCH_INACTIVE | bytes32 | keccak256("AREX:BATCH_INACTIVE") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | BATCH_EXPIRED | bytes32 | keccak256("AREX:BATCH_EXPIRED") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | BATCH_NOT_PRODUCED | bytes32 | keccak256("AREX:BATCH_NOT_PRODUCED") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | LIMIT_EXCEEDED | bytes32 | keccak256("AREX:LIMIT_EXCEEDED") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | DOCUMENT_MISSING | bytes32 | keccak256("AREX:DOCUMENT_MISSING") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | SEASON_CLOSED | bytes32 | keccak256("AREX:SEASON_CLOSED") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | IN_TRANSIT | bytes32 | keccak256("AREX:IN_TRANSIT") |
| Rejection codes (convention: keccak256("AREX:<NAME>")) | ATTESTATION_MISSING | bytes32 | keccak256("AREX:ATTESTATION_MISSING") |