CallbackHandler
Inherits: ICallbackHandler
Handles callback validation and execution for vault operations. This contract is designed to be used as a mixin in BaseVault, providing the ability to register logic for safely handling callbacks during guardian submissions. A common use case for handlers is receiving a flash loan. To receive a flashloan, the vault has to cede control when requesting a flashloan and then atomically handle the callback to repay the flashloan This requires two capabilities: the ability to register new handlers and the ability to initiate additional operations in the handle while being restricted by the merkle tree constraints. The callback handler contract achieves this by allowing guardians to "prepare" for a callback when they construct a given operation. If the operation "has a callback" then the fallback function in this contract will handle it. It will use transient storage to preserve information such as the expected callback caller, function selector of the callback and any approvals that are created during the callback
Uses transient storage to manage callback state and approvals
State Variables
CALLBACK_CALL_SLOT
ERC7201-compliant transient storage slot for storing the next authorized selector + caller
Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.call")) - 1)) & ~bytes32(uint256(0xff));
Note: security: Critical for callback validation
bytes32 internal constant CALLBACK_CALL_SLOT = 0xa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f00;CALLBACK_MERKLE_ROOT_SLOT
ERC7201-compliant transient storage slot for storing the callback merkle root
Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.merkleRoot")) - 1) & ~bytes32(uint256(0xff));
Note: security: Critical for callback validation
bytes32 internal constant CALLBACK_MERKLE_ROOT_SLOT = 0x30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe00;APPROVALS_SLOT
ERC7201-compliant transient storage slot for storing the approval tracking
Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.approvals")) - 1)) & ~bytes32(uint256(0xff));
Note: security: Critical for tracking token approvals during callbacks
Functions
fallback
Handle incoming callbacks and validates their authorization
Extracts callback data and forwards to _handleCallbackOperations if valid
_handleCallbackOperations
Internal handler for validated callbacks
Callback operations are like regular operations, but with a return value, which are encoded after operations array ┌─────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────┐ │ FIELDS │ SIZE │ DESCRIPTION │ ├─────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────┤ │ returnTypeFlag 1 byte 0 = no return, 1 = static, 2 = dynamic │ │ [if returnTypeFlag == 1]: │ │ returnDataLength 2 bytes Length of return data │ │ returnData bytes Static return data │ └───────────────────────────────────────────────────────────────────────────────────────────────────────┘
Parameters
root
bytes32
The merkle root of the callback
cursor
uint256
The cursor to the callback data
Returns
returnValue
bytes
The return value of the callback
_allowCallback
Whitelist a function selector and caller as a valid callback
Uses transient storage to store the callback data
Parameters
root
bytes32
The merkle root of the callback
packedCallbackData
uint256
Packed data containing caller, selector, and offsets
_storeCallbackApprovals
Store approvals for the current callback context
Uses transient storage to track approvals during callback execution
Length of the array, packed with the token address will be stored in the first slot
All other elements are laid out sequentially after the first slot, taking 2 slots per approval
If there are existing approvals, we will update length in the slot zero and append new approvals
Parameters
approvals
Approval[]
Array of token approvals to store
length
uint256
Length of the array
_getAllowedCallback
Retrieve the currently allowed callback data
Store packed token and length in the zero slot, and spender in the second
Update the length and preserve the token in the zero slot Minus one to compensate for pre-increment in upcoming storage loop
Unpacks data from transient storage
Note: security: Critical for callback validation
Returns
caller
address
The authorized caller address
selector
bytes4
The authorized function selector
userDataOffset
uint16
The offset in calldata where user data begins
_getAllowedMerkleRoot
Retrieves the currently allowed merkle root
Unpacks data from transient storage
Note: security: Critical for callback validation
Returns
root
bytes32
The authorized merkle root
_getCallbackApprovals
Retrieves the current callback approvals
Decodes approvals from transient storage
The first slot contains the length of the array, packed with the token address
All other elements are laid out sequentially after the first slot, taking 2 slots per approval
Only length slot is cleared, the rest of the approvals are left in the transient storage
This is safe because even if new approvals are added, old ones will be overwritten for length slots
Returns
approvals
Approval[]
Array of current token approvals
_hasCallbackBeenCalled
Checks if an expected callback has been called
If callback was expected but not received, CALLBACK_CALL_SLOT will not be reset to 0
Returns
<none>
bool
True if an expected callback has been called, false otherwise
_unpackCallbackData
Unpacks callback data from a packed uint256
Parameters
packed
uint256
The packed uint256 containing callback data
Returns
target
address
The target address
selector
bytes4
The function selector
dataOffset
uint16
The offset in calldata where user data begins
_packLengthAndToken
Packs a token address and length into a uint256
Used in transient storage slot zero
length is required to be less than type(uint96).max + 1
Parameters
length
uint256
The length of the approvals array
token
address
The token address
Returns
<none>
uint256
packed The packed uint256
Last updated

