betterERC
Docs/Reference

The transfer pipeline

Four phases in a fixed order, why the order is fixed in exactly one place, and what you can observe from outside.

All docs

Every balance change on the token — transfer, transferFrom, mint, burn — passes through a single internal update override. That override fixes the phase order, and it is the only one in the framework.

ERC20ExtensionCore.sol
1. restriction checks   // mint and burn arrive with the zero address intact
2. fee collection       // its own raw update, its own Transfer event
3. the transfer itself  // for value - fee
4. the hook             // after balances settle, guarded, under a gas cap

Why not super calls

The obvious alternative is for every module to override the update function and call super. Execution order would then fall out of C3 linearisation, so a token declared as fee-then-restriction would behave differently from one declared the other way round.

Worse, a fee module has to split a transfer into two balance movements, and both halves would descend through every module below it. A restriction module would end up screening the fee leg as though it were a user transfer, against the vault's address and the fee's amount — so freezing the fee vault would break every transfer on the token, for a reason nobody would think to look for.

Here, modules override phases instead. The order is fixed in one readable function, a module cannot change it by being listed first, and each phase receives the arguments it was designed for.

What you can observe

Rejection is total
Restriction runs first, so a transfer that is not allowed has not moved a fee, emitted an event, or called anything.
A frozen vault is harmless
The fee leg bypasses the phases entirely, so it is never screened as a user transfer. There is a test that pins exactly this.
Value is conserved in the log
The fee is a separate leg with its own Transfer event, so an indexer summing transfers sees no supply leak.
An exact balance still works
The fee leg runs before the main leg, so the two debits sum to what the sender holds rather than underflowing the second one.

Mint and burn reach phase one with the zero address intact, so a module can treat supply changes differently from transfers. Phases two and four are skipped for them entirely: a fee on minting invents supply, and a fee on burning is not a burn.