The Hook struct defines the parameters for user-specified hooks that execute during CoW Protocol settlements.
Definition
struct Hook {
address target;
bytes callData;
uint256 gasLimit;
}
The struct is defined within HooksTrampoline and specifies the parameters needed for a single hook call execution.
Fields
target
| Property | Value |
|---|
| Type | address |
| Required | Yes |
The recipient contract address for hook execution.
Requirements:
- Must be a valid Ethereum address
- Can be an EOA or a contract
- The call executes regardless of whether code exists at the address
callData
| Property | Value |
|---|
| Type | bytes |
| Required | Yes |
The encoded function signature and parameters.
Requirements:
- Can be empty (for plain ETH transfers)
- Should be ABI-encoded for function calls
- Use
abi.encodeWithSignature() or abi.encodeWithSelector() for encoding
gasLimit
| Property | Value |
|---|
| Type | uint256 |
| Required | Yes |
The maximum gas forwarded to the hook call.
Requirements:
- Must be sufficient for the hook execution
- Accounts for the 63/64 gas forwarding rule
- If insufficient gas remains, the trampoline reverts
- Setting too high risks reversion; setting too low causes hook failure
Code Examples
Basic Hook Construction
Hook memory hook = Hook({
target: 0x1234567890123456789012345678901234567890,
callData: abi.encodeWithSignature("afterTrade()"),
gasLimit: 50000
});
Hook with Parameters
Hook memory hook = Hook({
target: rewardsContract,
callData: abi.encodeWithSignature(
"distributeRewards(address,uint256)",
trader,
rewardAmount
),
gasLimit: 100000
});
Multiple Hooks Array
Hook[] memory hooks = new Hook[](3);
hooks[0] = Hook({
target: priceOracle,
callData: abi.encodeWithSignature("updatePrice(address)", token),
gasLimit: 75000
});
hooks[1] = Hook({
target: analyticsContract,
callData: abi.encodeWithSignature("recordTrade(uint256)", tradeVolume),
gasLimit: 50000
});
hooks[2] = Hook({
target: notificationContract,
callData: abi.encodeWithSignature("notifyTrader(address)", trader),
gasLimit: 30000
});
Empty Call Data Hook
Hook memory hook = Hook({
target: receiverContract,
callData: bytes(""),
gasLimit: 21000
});
Using Selector for Efficiency
Hook memory hook = Hook({
target: stakingContract,
callData: abi.encodeWithSelector(
IStaking.stake.selector,
stakeAmount,
duration
),
gasLimit: 80000
});
Gas Limit Considerations
Due to the EVM’s 63/64 rule for gas forwarding, the actual available gas to the hook may be slightly less than specified.
Calculating Gas Limits
- Estimate execution cost through testing
- Add 10-20% buffer as a safety margin
- Account for the trampoline’s
gasleft() * 63 / 64 calculation
- Consider that failed hooks consume the gas limit without blocking the settlement
Hook memory hook = Hook({
target: myContract,
callData: abi.encodeWithSignature("myFunction()"),
gasLimit: 55000
});
Common Patterns
Pre-Settlement Hook
Hook memory preHook = Hook({
target: userContract,
callData: abi.encodeWithSignature(
"beforeSettle(address,uint256)",
sellToken,
sellAmount
),
gasLimit: 100000
});
Post-Settlement Hook
Hook memory postHook = Hook({
target: userContract,
callData: abi.encodeWithSignature(
"afterSettle(address,uint256)",
buyToken,
buyAmount
),
gasLimit: 100000
});
Conditional Hook
Hook memory conditionalHook = Hook({
target: smartContract,
callData: abi.encodeWithSignature(
"handleTrade(address,address,uint256)",
sellToken,
buyToken,
amount
),
gasLimit: 150000
});
Best Practices
A hook that reverts due to insufficient gas or errors will not block the settlement, but it also won’t execute its intended logic.
- Test gas usage on testnets, measuring actual consumption
- Handle reverts gracefully in hook target design
- Minimize complexity to reduce gas costs and failure risk
- Validate inputs in hook target parameters
- Emit events for tracking execution and debugging