Callback Validation
The requirement that a contract implementing a protocol callback prove the caller is the genuine protocol contract before releasing any funds, typically by deriving the expected address and comparing it to msg.sender.
Callback validation is the check that separates a router from a faucet.
Protocols that transfer optimistically — Uniswap V3's mint, swap and flash among them — send tokens first and then call back into the caller demanding payment, verifying their own balance afterward. The integrator must therefore implement a public function that transfers tokens out. Anyone can call it. If it does not prove who is calling, anyone can drain whatever the contract holds or has been approved to spend.
The correct shape
In Uniswap V3's SwapRouter, the callback does four things in a fixed order:
- Require that at least one of the reported deltas is positive, since a real swap always leaves the pool owed something.
- Decode the callback data into the token pair, fee, and payer.
- Derive the pool address from the decoded tokens and fee using CREATE2.
- Require that
msg.senderis that derived address.
Only then does it transfer. Everything above that line is derivation, and nothing above it has moved a single wei.
Why deriving from attacker-controlled data is not circular
The objection is natural: the callback data is supplied by whoever initiated the call, and the check derives the expected pool from that same data. It looks like the attacker gets to choose their own examiner.
They do, and it does not help them. The check binds a claimed identity to the actual caller. If an attacker lies about the token pair, the derived address is some other address, not theirs. If an attacker names a real pool's tokens honestly, the derived address is that real pool, which is also not theirs. The only address that can ever satisfy the equality is the genuine pool for exactly those parameters — and genuine pools only emit callbacks mid-swap, with honest deltas.
The data is untrusted; the binding is what is trusted.
Why the payer field is safe
The same callback data carries a payer address whose approval the router spends. That is safe for a different reason: the router sets it. Only the router encodes that struct, and it always sets payer to the msg.sender of the outer entry point. It is not user input at all — it is the router's own data, returning through a call that has now been proven to come from a pool.
A fork that lets callers supply the payer directly, or that decodes it before the pool check, converts the router into a machine for spending other people's approvals.
The asymmetry worth remembering
The core protocol has to get this right once. Every integrator has to get it right again, in their own contract, with no help from the compiler. That asymmetry is why missing or reordered callback validation remains one of the most common findings in V3-integration audits, years after the pattern was documented.
Related Terms
CREATE2
An EVM opcode that deploys a contract to an address derived deterministically from the deployer, a salt, and the init code hash, making the address computable before deployment.
Reentrancy Attack
A vulnerability where external calls allow malicious contracts to recursively call back before state updates complete.
Flash Loan
Uncollateralized loan borrowed and repaid within a single transaction, often used for arbitrage or attacks.
Access Control
Security mechanisms that restrict which addresses can call specific functions in a smart contract, preventing unauthorized actions.
Need expert guidance on Callback Validation?
Our team at Zealynx has deep expertise in blockchain security and DeFi protocols. Whether you need an audit or consultation, we're here to help.
Get a Quote