Write Hooks
Quick Start
Requirements
Foundry ≥ v1.3.0Solidity 0.8.30pnpmyarn
Repo Setup
Clone the public repo and its submodules:
git clone --recursive https://github.com/superform-xyz/v2-core.git
cd v2-coreInstall dependancies:
forge install
cd lib/modulekit
pnpm install
cd ../..
cd lib/safe7579
pnpm install
cd ../..
cd lib/nexus
yarn
cd ../..Copy the environment file
Build & Testing
Compile the contracts
Run tests
Anatomy of a Hook
Every hook must inherit BaseHook to obtain core hook functionality, along with any specific interfaces your hook may require from ISuperHook.
Each hook contact begins with natspec detailing the layout of the hook data, this ensures that SuperBundler is able to encode the data required for hook execution.
The natspec of the data structure must be placed after the line saying
@dev data has the following structureand the natspec must follow the format of@notice DataType Name Offset.
The constructor sets the
HookTypeandHookSubtypeas well as any immutable state variables such as the target contract address to call during execution. For a breakdown of differentHookTypeoptions see this page, and theHookSubTypesare found in this library If your hook will perform an action type that is not yet in the library, add it there asbytes32 public constant CATEGORY_NAME.It is necessary to decode and validate the encoded hook data used by the hook. It is recommended to create some internal decoder and validation helpers. The decoding must align with the data locations denoted in the natspec as that is the order in which data will be encoded. There are two libraries you can use during the decoding process:
BytesLib— This necessary library be found in thesrc/vendordirectory, you need only import this it to gain access to its functionality.HookDataDecoder- This library lives in thesrc/librariesdirectory. It must be imported and the statementusing HookDataDecoder for bytesmust be placed at the top of the hook contract.
The
_buildHookExecutions()function takes decoded hook data and returns an array of Executions.There are 3 parameters for this function:
prevHook— The address of the prior hook in a chain,address(0)if this hook is unchained or the first in a chain. Note: If you intend for this hook to be after another hook in a chainbool usePrevHookAmountmust be added as a parameter to be encoded in the hook data structure natspec, andISuperHookResultis required.account— The smart account address if needed for the Execution payload.data— The bytes hook data (encoded in the structure defined by the natspec)
The payload of an Execution is as follows:
Target— The address to call (contract or another module)Value— Native ETH amount to forward asmsg.valueCalldata— The encoded function selector to be called and any arguments
Hook Contract Outline
Key fields inherited from BaseHook:
hookType
Enum for accounting engine
subType
32‑byte tag for analytics / routing
usedShares, spToken, asset
Transient storage outputs consumed by later hooks
setOutAmount(uint256,address)
Records how many units this hook produced
See the full base contract for inline docs here.
Writing your First Hook: Approve‑and‑Deposit ERC‑4626
We’ll re‑create the example at
src/hooks/vaults/4626/ApproveAndDeposit4626VaultHook.sol GitHub.
Approve the vault, deposit amount into it, and emit the number of shares received so that downstream hooks can compose on it.
Hook Data layout
0
32
yieldSourceOracleId (optional)
32
20
yieldSource (IERC4626 vault)
52
20
token (underlying)
72
32
amount (uint256)
104
1
usePrevHookAmount (bool)
Implementation Highlights
To test the newly created hook follow the steps outlined here.
Last updated

