Skip to main contentA-REX Core

Registros de decisiones · ADR-004

ADR-004: One hook interface, flags cached at registration, ordered evaluation

Every rule is an independent IARexHook contract; the token calls the hooks in registration order, using flags cached at registration, and stops at the first rejection.

Accepted
ID del documento
ADR-004
Fecha
2026-09-21
Disponible solo en inglés
Este documento se publica en inglés. Los capítulos del whitepaper y los diagramas están traducidos; el estándar normativo, las guías de diseño de sistema y los registros de decisiones permanecen en inglés para que exista un único texto vinculante.
FieldValue
IDADR-004
Subject areaExtensibility
StatusAccepted
Date2026-09-21
OwnersA-REX Contributors
Related requirementsFR-07, FR-08, NFR-01, NFR-02, NFR-10 (SDD §3)
Supersedes / superseded by—

Context

Perishability, harvest seasons, custody during delivery, quotas, sanctions and document requirements all decide whether units may move, and no issuer needs all of them. The earlier design probed each module for five capability interfaces with supportsInterface on every transfer, so the cost grew as ids × modules × probes before any rule ran, and a module written for one token could not run on another.

Issue

How do rules attach to the token so that cost stays linear, order is auditable and one rule contract can serve many tokens?

Assumptions

  • Rules differ per issuer; none needs all of them.
  • Gas per transfer must be linear in the number of rules actually attached.
  • The order in which rules run is a governance decision that must be visible.

Constraints

  • Rules must not be able to change balances or re-enter the token.
  • The token must stay under the EIP-170 size limit.

Alternatives considered

AlternativeForAgainstOutcome
Several capability interfaces probed per transferFine-grainedExternal calls before any logic; not portable between tokensRejected
ERC-3643 ICompliance adapted to ERC-1155Known to security-token issuersERC-20 semantics, no batch id, no transfer dataRejected
Single IARexHook (hookFlags, check, onExecuted, canSend, canReceive) with cached flagsOne call per interested hook; portable; orderedHooks must follow a strict callback contractAccepted

Decision

A conformant token keeps an ordered list of hooks and caches each hook's hookFlags() when it is registered. For every affected id it calls check on every CHECK hook, in order, and reverts with ARexHookRejected(hook, code) at the first non-zero code; after balances change it calls onExecuted on every EXECUTED hook; ELIGIBILITY hooks answer canSend and canReceive. A hook learns which token is calling from msg.sender, so one deployment serves many tokens.

Argument

Caching the flags turns "which capabilities does this module have" from a per-transfer question into a one-time one. A single callback contract is also what makes a rule portable: any conformant token can register any conformant hook.

Consequences

Positive

  • Measured cost of about 25–30k gas per CHECK hook on a transfer, linear in the number of hooks (SDD §10).
  • Rules can be added and removed without touching the token.

Negative

  • A buggy or malicious hook can block transfers until it is removed; hook registration is a trusted act.
  • Changing a hook's flags requires re-registration.

Obligations it creates

  • HOOK_ADMIN_ROLE belongs behind a timelock so that hook changes are visible before they apply.
  • Hooks must not revert for policy (ADR-005) and must return OK for forced transfers unless they govern enforcement (ADR-012).
  • The token holds a reentrancy lock across the whole sequence.

Implementation and evidence

WhatWhere
PipelineARexToken._update, registerHook, removeHook
Order, caching, rejection, data, reentrancytest/standard/ARexToken.test.js: registers hooks in order, caches flags and removes preserving order, stops at the first rejecting hook…, forwards transfer data to hooks, blocks reentrancy from onExecuted
Gas per hookscripts/gas-report-standard.js
  • ADR-001: Standardize interfaces only; modules stay outside the standard
  • ADR-005: bytes32 rejection codes instead of strings or hook-side custom errors
  • ADR-006: Open codes instead of closed enumerations
  • ADR-012: Forced transfers bypass pause and eligibility but still run hooks