Diagrams · 03
Transfer pipeline
Every balance change in the reference token passes through one function. The order below is fixed; transferCheck and canTransfer replay it as views.
Sequence of a transfer with two hooks
sequenceDiagram
autonumber
participant U as Holder / operator
participant T as ARexToken._update
participant E as EligibilityHook (CHECK, ELIGIBILITY)
participant X as ExpiryHook (CHECK)
participant R as TraceabilityHook (EXECUTED)
U->>T: safeTransferFrom(from, to, id, amount, data)
T->>T: lock (reentrancy) · not paused
T->>E: canSend(from) / canReceive(to)
E-->>T: true / true
T->>T: batch exists · status != RETIRED · amount ≤ balance − frozen
T->>E: check(OP_TRANSFER, operator, from, to, id, amount, data)
E-->>T: OK
T->>X: check(OP_TRANSFER, …)
X-->>T: OK
T->>T: super._update (balances, supply)
T->>R: onExecuted(OP_TRANSFER, …)
R-->>T: emit EPCIS-shaped event
T->>T: unlock
T-->>U: TransferSingleWhat happens on rejection
flowchart LR
A[check returns code ≠ 0] --> B["revert ARexHookRejected(hook, code)"]
B --> C[no balance changed]
B --> D[no TransferSingle emitted]
B --> E["front-end maps code → message<br/>e.g. keccak256('AREX:BATCH_EXPIRED')"]Evaluation order and who can skip what
| Step | Holder transfer | forcedTransfer (ENFORCER_ROLE) | transform |
|---|---|---|---|
| lock | yes | yes | yes |
| pause | blocks | does not block | blocks |
canSend / canReceive | yes | skipped | yes (inputs from from, outputs to to) |
| frozen balance | enforced | lowered to balance − amount | enforced |
hooks check | OP_TRANSFER | OP_FORCED_TRANSFER | OP_TRANSFORM for inputs and outputs |
hooks onExecuted | yes | yes | yes |