Skip to main contentA-REX Foundation

سجلات القرارات · ADR-005

ADR-005: bytes32 rejection codes instead of strings or hook-side custom errors

A hook refuses by returning a namespaced bytes32 code; the token reverts with ARexHookRejected(hook, code) and exposes the same code before the transaction through transferCheck.

Accepted
معرّف المستند
ADR-005
التاريخ
2026-09-21
متوفر بالإنجليزية فقط
هذه الصفحة بالإنجليزية. فصول الورقة البيضاء والمخططات مترجمة. يبقى المعيار المعياري في المستودع؛ استخدم طلب وصول مبكر لذلك النص. وتبقى أدلة تصميم النظام وسجلات القرارات بالإنجليزية.
FieldValue
IDADR-005
Subject areaError model
StatusAccepted
Date2026-09-21
OwnersA-REX Contributors
Related requirementsFR-09 (SDD §3)
Supersedes / superseded by—

Context

A farmer's wallet has to say why a transfer failed, an indexer has to count refusals by reason, and a test has to compare them. Free-form revert strings are expensive, untranslatable and not indexable. If hooks reverted with their own custom errors, a view that asks several hooks, such as canTransfer, could not compose their answers.

Issue

How does a hook communicate a refusal so that the token, user interfaces and indexers can act on it?

Assumptions

  • Front-ends must explain refusals in the user's language.
  • Indexers and tests must compare refusals exactly.

Constraints

  • check must be a view so that pre-flight checks can call it.

Alternatives considered

AlternativeForAgainstOutcome
Free-form revert stringsReadable in explorersCostly, not indexable, not translatable, not composableRejected
Hooks revert with custom errorsTypedcheck could not be composed in a view across hooksRejected
bytes32 codes keccak256("NAMESPACE:NAME"); token reverts with ARexHookRejected(hook, code)Cheap, comparable, indexable, open to new namespacesNeeds a dictionary to show textAccepted

Decision

check returns bytes32; zero means allowed. Codes are keccak256("NAMESPACE:NAME"); the recommended AREX: codes live in ARexCodes. The token reverts with ARexHookRejected(address hook, bytes32 code) and exposes transferCheck, which returns the first code, in the same order as the transaction.

Argument

A 32-byte code is the cheapest value that is both collision-resistant and open to anyone's namespace. Naming the rejecting hook in the error tells an operator which rule to look at without a trace.

Consequences

Positive

  • Wallets can show a translated reason before sending a transaction.
  • Issuers add codes without coordinating with anyone.

Negative

  • Front-ends need a code dictionary; unknown codes show as hashes.

Obligations it creates

  • transferCheck and the transaction evaluate in the same order (regression test).
  • Hooks document their codes.

Implementation and evidence

WhatWhere
Codescontracts/standard/ARexCodes.sol
ErrorIARexHook.ARexHookRejected
Same order in view and transactiontest/standard/ARexToken.test.js: reports eligibility before batch existence, like _update
  • ADR-004: One hook interface, flags cached at registration, ordered evaluation
  • ADR-006: Open codes instead of closed enumerations