betterERC
All posts
InternalsJune 18, 20268 min read

The hook is the expensive one

A transfer hook is the most costly thing this framework lets a token do to the people integrating with it. So it is built to be survivable rather than flexible.

Every other extension changes an amount or refuses a transfer. A hook hands control to a contract the token author chose, in the middle of somebody else's transaction. It is the one feature here that can fail for reasons having nothing to do with balances, allowances, or anything else the caller can inspect beforehand.

Which is why the design goal was never flexibility. It was survivability: whatever the hook does, the integrator's failure mode should be bounded and predictable.

Last
Phase four, after balances have settled. The hook cannot observe a half-applied transfer and cannot influence the amounts — only accept the result or reject it.
Guarded
The whole pipeline runs inside a transient-storage reentrancy guard, so a hook calling back into any balance-moving path reverts. The fee module's internal leg reaches the base implementation directly and never re-enters, so splitting a transfer in two does not trip the guard on itself.
Bounded
The hook receives a published gas budget and no more, so an integrator can price a worst case instead of discovering one.
Strict
A reverting hook reverts the transfer. So does a hook that returns without the acknowledgement selector. Swallowing failures would turn a policy contract into decoration.

The budget is not quite the budget

The published limit is what the hook receives, not what the caller must hold. The EVM withholds one sixty-fourth of the remaining gas from any call, so a caller holding exactly the published number will watch the hook receive slightly less and run out. This is documented rather than papered over, because a caller who knows the rule can budget around it and a caller who does not would have found out the hard way.

The limit is floored and ceilinged too. Below thirty thousand a hook cannot do anything useful, and above a million the published number stops being a bound anyone would treat as one.

The configuration mistake that bricks a token

Pointing the hook at an address with no code would pass every check at configuration time and then fail on every transfer forever. A call to an account with no code succeeds and returns nothing, the acknowledgement check sees nothing, and the transfer reverts. The token would be dead, and the transaction that killed it would have looked entirely fine.

So the setter checks for code, and the mistake becomes a failed configuration call instead of a dead token. Removing the hook forces the published budget back to zero in the same move, so the token never advertises an allowance for a call it will not make.

Strictness is the honest choice here. The flag exists precisely to warn that transfers on this token can fail for reasons unrelated to balances.