betterERC
Docs/Integrating

Transfer hook

An external call on the transfer path: what it can and cannot do, how much gas to hold, and why a failed transfer no longer means insufficient funds.

All docs

The token calls a policy contract after every transfer between two real accounts. It is the most expensive thing a token in this framework can do to you, and the flag exists to warn you before you commit rather than after.

What the hook can and cannot do

It runs last
After balances have settled. It cannot change amounts and cannot observe a half-applied transfer — only accept the result or reject it.
A revert reverts
Transfers can fail for reasons having nothing to do with balances or allowances. Do not assume a failed transfer means insufficient funds.
It cannot re-enter
The token holds a transient-storage reentrancy guard for the whole pipeline, so a hook calling back into any balance-moving path reverts.
It is inspectable
transferHook() names the contract, so you can read its code before integrating rather than discovering it in a trace.
Supply is exempt
Mint and burn do not fire the hook; phase four only runs for transfers between two non-zero addresses.

Budgeting the gas

The hook receives exactly the published limit. The EVM withholds one sixty-fourth of the remaining gas from any call, so a caller holding exactly that number will watch the hook receive slightly less and run out. Hold roughly the budget times sixty-four over sixty-three, on top of what a plain transfer costs.

Router.sol
if (flags & TRANSFER_HOOK != 0) {
    uint32 budget = IERC20TransferHook(token)
        .transferHookGasLimit();
    require(
        gasleft() > baseCost + (uint256(budget) * 64) / 63,
        "insufficient gas for hook"
    );
}

The budget is bounded on both sides: never below thirty thousand, because a hook with less cannot do anything useful, and never above a million, because past that the published number stops being a bound anyone would treat as one. A token with the module installed but no hook configured reports a budget of zero.

A hook that returns without the expected acknowledgement selector reverts the transfer too. Swallowing hook failures would turn a policy contract into decoration, and the flag exists precisely to say that transfers here can fail for external reasons.