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

bytes32 internal constant APPROVALS_SLOT = 0xba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c00;

Functions

fallback

Handle incoming callbacks and validates their authorization

Extracts callback data and forwards to _handleCallbackOperations if valid

fallback(bytes calldata) external returns (bytes memory returnValue);

_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 │ └───────────────────────────────────────────────────────────────────────────────────────────────────────┘

function _handleCallbackOperations(bytes32 root, uint256 cursor) internal virtual returns (bytes memory returnValue);

Parameters

Name
Type
Description

root

bytes32

The merkle root of the callback

cursor

uint256

The cursor to the callback data

Returns

Name
Type
Description

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

function _allowCallback(bytes32 root, uint256 packedCallbackData) internal;

Parameters

Name
Type
Description

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

function _storeCallbackApprovals(Approval[] memory approvals, uint256 length) internal;

Parameters

Name
Type
Description

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

function _getAllowedCallback() internal returns (address caller, bytes4 selector, uint16 userDataOffset);

Returns

Name
Type
Description

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

function _getAllowedMerkleRoot() internal returns (bytes32 root);

Returns

Name
Type
Description

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

function _getCallbackApprovals() internal returns (Approval[] memory approvals);

Returns

Name
Type
Description

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

function _hasCallbackBeenCalled() internal view returns (bool);

Returns

Name
Type
Description

<none>

bool

True if an expected callback has been called, false otherwise

_unpackCallbackData

Unpacks callback data from a packed uint256

function _unpackCallbackData(uint256 packed)
    private
    pure
    returns (address target, bytes4 selector, uint16 dataOffset);

Parameters

Name
Type
Description

packed

uint256

The packed uint256 containing callback data

Returns

Name
Type
Description

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

function _packLengthAndToken(uint256 length, address token) private pure returns (uint256);

Parameters

Name
Type
Description

length

uint256

The length of the approvals array

token

address

The token address

Returns

Name
Type
Description

<none>

uint256

packed The packed uint256

Last updated