Skip to main contentA-REX Foundation

Whitepaper · 08

Security Framework

Threat model, invariants, access control, the hook trust boundary, enforcement key management, upgrade safety and audit posture of the A-REX Core reference implementation.

Security posture in one paragraph

The reference token has one choke point (_update), a fixed evaluation order, a reentrancy lock around the entire hook sequence, role-based authority that is never derived from balances, ERC-7201 storage, and a small surface: eight external state-changing functions beyond ERC-1155. The most powerful capabilities — freezing, forced transfer, hook administration, upgrades — are separate roles intended for multisigs and timelocks, and every use of them is an event.

Invariants

InvariantEnforced byTested by
totalSupply(id) equals the sum of balances for idOpenZeppelin ERC1155Supply; transform burns then mints through _updatelineage tests (split, merge, processing)
status == 255 is terminalsetBatchStatus reverts with ARexInvalidStatusTransitionstatus tests
A retired batch cannot be minted or transferred, only burned_updateretired-batch test
amount ≤ balance − frozen for holder-initiated moves_update; forcedTransfer lowers frozen to balance − amountfreeze/forced tests
No hook can re-enter a balance-changing functionstorage lock set before checks, cleared after notificationsReentrantHook test
Every balance change is evaluated as its own operation, with its own data_update reads and clears the operation context before any check or external callOpContext.test.js
Hooks are evaluated in registration order; removal preserves orderarray shift in removeHookordering test
A rejected operation changes no balance and emits no transferchecks run before super._updaterejection test (executed count unchanged)
Authority never derives from balanceevery state-changing function is role-gated or ERC-1155-approval-gated"holding tokens grants nothing" tests
canTransfer == (transferCheck == 0)canTransfer is defined as thatconformance tests

Threat model

Actors and capabilities

ActorCanCannot
Holdertransfer unfrozen units to eligible receivers; burn; re-batch same commodity/unit with conserved quantity into its own account (to == from)change status, mint, process into another commodity, re-batch into another account, move frozen units
Approved operatorsame as holder for the approving accountanything a holder cannot
ISSUER_ROLEcreate batchesmove others' tokens
TRANSFORMER_ROLEprocessing transforms, and transforms whose outputs go to another account, of tokens it holds or is approved fortransform tokens without approval
STATUS_ROLEchange status (including retire), update metadatamove tokens
ENFORCER_ROLEfreeze any amount; force any transferbypass hooks (they run with OP_FORCED_TRANSFER)
HOOK_ADMIN_ROLEadd/remove hookschange balances directly
PAUSER_ROLEhalt everything except forced transfers—
UPGRADER_ROLEreplace implementation— (this is total power; see key management)
ATTEST_ADMIN_ROLEdecide which issuers are trusted for each attestation topic; in the reference releases up to 2.0.0-alpha.4, also revoke any attestation (ADR-019, Proposed: an administrative revoke SHOULD NOT exist in 2.x and MUST NOT from 3.0)attest in an issuer's name
Trusted issuerattest batches for its topics; revoke its own attestations, or delegate revocationattest for a topic it is not trusted for
Hook contractreject operations; observe them; answer eligibilityre-enter; change balances

Scenarios

Malicious or buggy hook (denial of service). A hook that always rejects blocks all transfers of the token; one that reverts in check does the same. Mitigation: HOOK_ADMIN_ROLE can remove any hook at any time (order preserved); PAUSER_ROLE can halt while investigating; forced transfers still run, so enforcement is not lost. Registration verifies ERC-165 support and non-zero flags, and rejects EOAs. Recommendation: audit hooks before registering; keep the number small; put the admin behind a timelock so that hook changes are visible before they take effect.

Hook re-entrance. A hook's onExecuted calls back into safeTransferFrom. Mitigation: the lock; the call reverts with ARexReentrantCall. Note that ERC-1155's receiver callback (onERC1155Received) runs after the lock is released, as in every ERC-1155 token; the token's state is final by then.

Operation context in receiver callbacks. A contract recipient starts a transfer or a burn from inside onERC1155Received. That nested call is an OP_TRANSFER or OP_BURN with its own data, not part of the operation that delivered the tokens. Up to 2.0.0-alpha.2 the reference token kept the outer operation in storage until the outer call returned, so a nested call after a forcedTransfer ran as OP_FORCED_TRANSFER (skipping pause, eligibility and frozen balances), and one after a transform ran as OP_TRANSFORM, which hooks that gate only OP_TRANSFER let through. Mitigation: _update reads and clears the operation context before any check or external call, so a call made from a callback is evaluated as itself; OpContext.test.js uses a recipient that transfers and burns from inside its callback.

Delivery by re-batching. Both legs of a transform run hooks with OP_TRANSFORM. If a holder could re-batch into another account, goods would change hands without the checks of hooks that gate only OP_TRANSFER, such as an attestation required before sale or units locked for a delivery. Mitigation: without TRANSFORMER_ROLE, a re-batch is allowed only when to == from; re-lotted goods reach another holder through an ordinary transfer that runs every transfer rule (ADR-007, amended 2026-10-20).

Escaping a lock by burning or re-lotting. Hook state kept per batch id does not follow a lot into its outputs. A hook that locks units and checks only OP_TRANSFER does not stop the holder from burning the locked units, or from re-lotting them in place into a new id that the lock does not cover. Mitigation: DeliveryHook rejects with IN_TRANSIT any burn or transform input leg that would take the balance below the locked units; forced transfers stay unlimited, and the hook returns a code instead of reverting after a seizure. Hooks that reserve units should check OP_BURN and OP_TRANSFORM as well as OP_TRANSFER.

Compromised enforcement key. Whoever holds ENFORCER_ROLE can move any tokens. Mitigation: multisig with a documented procedure (court order, pledge agreement, sanctions notice); every action emits ForcedTransfer/Frozen; hooks can require an order reference in transfer data (OP_FORCED_TRANSFER is not exempt from hooks). Consider a timelock for non-urgent enforcement.

Compromised upgrader. Total compromise. Mitigation: multisig plus timelock; OpenZeppelin upgrade validations in CI; ERC-7201 storage to avoid layout accidents.

Status abuse. STATUS_ROLE can retire a batch held by others. This is intentional — a warehouse, inspector or regulator declaring a lot unfit — and must be disclosed to holders. Mitigation: assign to an accountable institution; every change is an event with a reason.

Issuer fraud. ISSUER_ROLE can create a batch that does not exist physically. The token cannot prevent this; it makes it attributable (issuer, originRef, metadataHash) and lets attestations from independent parties (warehouse, inspector, registry) be required before transfer via AttestationHook.

Location hidden in the origin reference. A hash, a geohash or any other encoding of plot coordinates in originRef looks anonymous but can be recomputed from a guessed plot, and it can never be erased from the ledger. Mitigation: originRef is a reference to evidence or a random reference; geolocation stays off-chain (for the EUDR, in the EU Information System). The draft proposed with ADR-019 (Proposed) makes "never derived from location" a MUST; it was already the decision of ADR-008.

Unverified upstream references. A batch made from another token's batch names it in the upstream list of its metadata. That is the downstream issuer's claim: it can name a batch it never held or leave out one it did. Mitigation: indexers, auditors and recall tools check each reference against the upstream token (the batch exists, the metadata hash matches, the transfer to the issuer happened when a transaction hash is given) and show the ones they could not check as unverified.

Metadata substitution. setBatchMetadata changes the hash and URI. Mitigation: role-gated; BatchMetadataUpdated and URI events keep the history; earlier documents remain verifiable against earlier hashes.

Front-running of transferCheck. A pre-flight check can become stale before the transaction executes (a hook's state changes). The transaction re-evaluates; the worst case is a revert with a precise code.

Gas griefing through hooks. Each hook adds one external call per affected id per phase. A hook with unbounded loops makes transfers unaffordable. Mitigation: hook authoring rules (bounded check), review before registration.

Upgradeable hook trust. A hook deployed behind a proxy can change transfer policy without any change on the token. Mitigation: treat hook admin keys with the same care as HOOK_ADMIN_ROLE; prefer immutable hooks for production.

Access control model

OpenZeppelin AccessControl with eight roles on the token and one on the attestation registry; DEFAULT_ADMIN_ROLE administers all of them. Recommended production layout:

RoleHolder
DEFAULT_ADMIN_ROLE, UPGRADER_ROLE, HOOK_ADMIN_ROLEgovernance multisig behind a timelock
ENFORCER_ROLEenforcement multisig (compliance + legal)
PAUSER_ROLEincident-response key (fast)
ISSUER_ROLE, STATUS_ROLE, TRANSFORMER_ROLEoperational services with least privilege, rotated
ATTEST_ADMIN_ROLEthe token's governance: it decides which issuers are trusted per topic, not what they attest

initialize grants everything to one admin for convenience; the deployment checklist requires redistribution before production.

Enforcement and pause semantics

  • Pause blocks mints, transfers, burns and transforms; it does not block forced transfers, so a lawful order can be executed during an incident.
  • Forced transfers skip canSend/canReceive and the frozen-balance check, lower the frozen amount to what remains, and still run hooks with OP_FORCED_TRANSFER. Reference hooks return OK for that op; a hook that governs enforcement may require order references in data.
  • Freezing may exceed the balance (ERC-7943), so a lien can cover future deposits.

Capabilities in the standard, policy in profiles

ADR-019 (Proposed) draws one line through every enforcement feature. The standard fixes what exists and what it guarantees: the roles, the events, what a pause stops, the codes AREX:PAUSED and AREX:SANCTIONED, who may revoke an attestation, and the privacy rules. A policy profile, kept per jurisdiction and per organization and versioned with dates like a regulation, says who may use each capability and under which conditions:

CapabilityFixed by the standardSet by the policy profile
Freeze and forced transfersetFrozenTokens, forcedTransfer, their events, a dedicated role separate from administration, hooks run with OP_FORCED_TRANSFERwhether the role may be enabled at all, who holds it and with how many signers, which legal reference each use must carry, whom to notify
Pausewhat a pause stops, AREX:PAUSED, forced transfers still allowedwho may pause and unpause, the confirmation and reason required, who is told
SanctionsAREX:SANCTIONED and the meaning of canSend / canReceivewhich official lists apply (UN, OFAC, EU, OFSI or a combination), how they are kept current, and that a person confirms a match before an account is marked
Attestation revocationthe issuer revokes, a revoker it delegated may; an administrative role should not in 2.x and must not from 3.0which issuers a token trusts per topic; how an operator records a dispute
Origin privacyoriginRef holds no coordinates or personal identifiers and is not derived from locationwhich reference an issuer uses, and where geolocation is kept off-chain

A change of law then becomes a profile update, never a new contract or a new version of the standard.

Upgrade safety

  • UUPS; _authorizeUpgrade gated by UPGRADER_ROLE.
  • ERC-7201 namespaced storage struct; append-only.
  • OpenZeppelin upgrades plugin validations run in tests and should run in CI before any upgrade transaction.
  • The per-file optimizer.runs = 1 setting keeps the implementation under EIP-170; a change that pushes it over fails at compile time with a warning and at deployment with a revert.

Dependency and build security

  • OpenZeppelin Contracts and Contracts-Upgradeable pinned to 5.3.0; lockfile committed; CI uses npm ci.
  • Solidity 0.8.22, viaIR, EVM paris (no transient storage dependence, deployable on chains that have not adopted Cancun).
  • No external libraries linked; no delegatecall outside the UUPS proxy.

Audit posture

  • 46 tests on the reference implementation, hooks and extensions cover every invariant above, including a seeded randomized invariant run.
  • Gas and size are measured by scripts in the repository.
  • A third-party audit of ARexToken, ARexAttestations and the reference hooks is a stated precondition for a production anchor and for submitting the ERC (see Future Developments).
  • Formal verification of the pipeline invariants is on the roadmap; the invariants are small enough to be stated as properties.

Security advisories

AdvisoryAffectsFixed inIssues
AREX-2026-001reference implementation up to and including 2.0.0-alpha.22.0.0-alpha.3operation context leaked into ERC-1155 receiver callbacks; re-batching without a role could deliver goods to another holder past OP_TRANSFER hooks; DeliveryHook locks could be escaped by burning or re-lotting

2.0.0-alpha.3 supersedes 2.0.0-alpha.2. Deployed token proxies are upgraded to the new implementation through UPGRADER_ROLE; the storage layout is unchanged. DeliveryHook is not upgradeable: deploy the new version, register it with registerHook, remove the old one with removeHook, and re-schedule active deliveries, because their locks live in the old hook's storage. Integrations that split or merge straight into a buyer's account must re-lot in place and then transfer, or use an account with TRANSFORMER_ROLE. The interfaces of the standard do not change.

Residual risks the standard does not remove

  • The physical existence and quality of a lot are attested, not proven; the token makes claims attributable, not true.
  • Off-chain metadata availability depends on the storage chosen; the hash guarantees integrity, not availability.
  • Hook and module governance is only as good as the keys that control it.