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
| Invariant | Enforced by | Tested by |
|---|---|---|
totalSupply(id) equals the sum of balances for id | OpenZeppelin ERC1155Supply; transform burns then mints through _update | lineage tests (split, merge, processing) |
status == 255 is terminal | setBatchStatus reverts with ARexInvalidStatusTransition | status tests |
| A retired batch cannot be minted or transferred, only burned | _update | retired-batch test |
amount ≤ balance − frozen for holder-initiated moves | _update; forcedTransfer lowers frozen to balance − amount | freeze/forced tests |
| No hook can re-enter a balance-changing function | storage lock set before checks, cleared after notifications | ReentrantHook test |
| Hooks are evaluated in registration order; removal preserves order | array shift in removeHook | ordering test |
| A rejected operation changes no balance and emits no transfer | checks run before super._update | rejection test (executed count unchanged) |
| Authority never derives from balance | every state-changing function is role-gated or ERC-1155-approval-gated | "holding tokens grants nothing" tests |
canTransfer == (transferCheck == 0) | canTransfer is defined as that | conformance tests |
Threat model
Actors and capabilities
| Actor | Can | Cannot |
|---|---|---|
| Holder | transfer unfrozen units to eligible receivers; burn; re-batch same commodity/unit with conserved quantity | change status, mint, process into another commodity, move frozen units |
| Approved operator | same as holder for the approving account | anything a holder cannot |
ISSUER_ROLE | create batches | move others' tokens |
TRANSFORMER_ROLE | processing transforms of tokens it holds or is approved for | transform tokens without approval |
STATUS_ROLE | change status (including retire), update metadata | move tokens |
ENFORCER_ROLE | freeze any amount; force any transfer | bypass hooks (they run with OP_FORCED_TRANSFER) |
HOOK_ADMIN_ROLE | add/remove hooks | change balances directly |
PAUSER_ROLE | halt everything except forced transfers | — |
UPGRADER_ROLE | replace implementation | — (this is total power; see key management) |
| Hook contract | reject operations; observe them; answer eligibility | re-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.
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.
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:
| Role | Holder |
|---|---|
DEFAULT_ADMIN_ROLE, UPGRADER_ROLE, HOOK_ADMIN_ROLE | governance multisig behind a timelock |
ENFORCER_ROLE | enforcement multisig (compliance + legal) |
PAUSER_ROLE | incident-response key (fast) |
ISSUER_ROLE, STATUS_ROLE, TRANSFORMER_ROLE | operational services with least privilege, rotated |
ATTEST_ADMIN_ROLE | certification authority or platform |
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/canReceiveand the frozen-balance check, lower the frozen amount to what remains, and still run hooks withOP_FORCED_TRANSFER. Reference hooks returnOKfor that op; a hook that governs enforcement may require order references indata. - Freezing may exceed the balance (ERC-7943), so a lien can cover future deposits.
Upgrade safety
- UUPS;
_authorizeUpgradegated byUPGRADER_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 = 1setting 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,ARexAttestationsand 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.
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.