What a transfer costs
Measured, not estimated. What the pipeline adds, what each behaviour adds, and why discovery adds nothing at all.
All docs
Every figure below is produced by the test suite rather than estimated. Warm path, both accounts already funded, measured as a gas delta around the call. Cold-slot costs are excluded deliberately: they are paid once per pair of accounts and would drown the differences the table exists to show.
| Configuration | Gas | Delta |
|---|---|---|
| OpenZeppelin ERC20.transfer (baseline) | 3,906 | — |
| ExtendedToken, nothing switched on | 7,464 | +3,558 |
| ExtendedToken, fee active | 12,671 | +5,207 |
| ExtendedToken, hook installed | 9,895 | +2,431 |
| ExtendedToken, fee and hook | 15,102 | — |
What the baseline buys
The 3,558 is the pipeline itself: phase dispatch plus the registry reads that make the ordering guarantee possible. It does not depend on the amount, on the accounts, or on how many modules are installed, and it is the same on the millionth transfer as on the first. On a rollup, where the cost that matters is calldata rather than execution, it is close to noise.
A runtime clone pays a little more than the rows above, because it inherits every module and runs the transient-storage reentrancy guard on every transfer whether or not it installed the hook. Gating that on the hook was measured and costs more than it saves.
Discovery is free
extensions, behaviorFlags, extensionData and accountState are not on the transfer path, so reading a token's declarations costs a transfer nothing at all. There is a test that reads every discovery surface the token has and then measures the same transfer again, asserting the figure is identical.
What is not free is the fee and the hook — and those are exactly the things the behaviour word warns you about before you commit.