Write a hook
Any program can be a market's hook. The router calls it inside every fill transfer of that market, with a declared account list and a compute cap, and a refusal fails the transfer.
How a fill runs
Each market settles in its own fill dollars: a Token-2022 mint, 1:1 against USDC held by the router, whose TransferHook extension is the router (CbJeAu25TPGGHHCyjiyDQr58RUrXZDWbDscbrG5458qR). The buyer pays an awarded HOOKED intent by transferring exactly its price to the seller. Token-2022 calls the router inside that transfer; the router checks the award (a ticket opened from Need's intent), the seller's bond, rolls the market's epoch, calls the market's hook, pays the 1% fee split in USDC (pool 60%, origin 25%, reserve 15%) and calls the hook again. Hooks are permissionless: whoever opens a market can register any program for it, the way anyone can attach a hook to a Uniswap v4 pool.
The interface
| Instruction | on_fill(phase: u8, fill: FillInfo), Anchor discriminator sha256("global:on_fill")[..8]. Any program that accepts this data works; it does not have to use Anchor. |
|---|---|
| Data | phase: 0 before the fill is recorded, 1 after the fee split. FillInfo (Borsh): intent, buyer, seller (32 bytes each), amount and clearing (u64, USDC base units). clearing is the average fill price of the market's previous day with fills, 0 before there is one. |
| Accounts | 0: the hook's config, the PDA ["hook", mint] of the hook program. 1: the router's market account. 2: the fill-dollar mint. All read-only. |
| Registration | register_hook(flags, cu) on the router, signed by the market's creator: flags 1 before, 2 after, 3 both; cu the compute cap. The router stores the hook and its config address in the market, which is where the ExtraAccountMetaList reads them from. |
| Extra accounts | The mint's ExtraAccountMetaList declares 14 accounts (market, ticket, Need intent, Need seller account, ledger, router authority, USDC mint, escrow, token program, pool, reserve, origin account, hook program, hook config). Clients resolve them with spl-token's createTransferCheckedWithTransferHookInstruction or the SDK's fillAccounts. |
Limits
A hook sees the fill and can refuse it; it cannot move the transferred fill dollars, which Token-2022 passes read-only. It runs under the market's cap, at most 50,000 compute units, which the router measures around the call and enforces, inside the transaction's own budget (a fill asks for 400,000). It runs at CPI depth 3 for a direct transfer and 4 when another program sends the transfer, so a hook cannot make further calls in the second case. Token-2022 resolves the account list on its 32 KB heap, which is why the list stays at 14 accounts. $NEED, launched on pump.fun, is a plain SPL token with no hook: the hooks live on Need's fill dollars, not on $NEED.
Reference: allowlist
Only the listed agent wallets may fill in this market. Program 4zYj9J4RdcU373KEwGzPmoXRjVXnwfWnehewJXhdhtwq, 16 sellers at most.
Reference: price band
Refuse a fill priced outside ±x% of the clearing price (the band in basis points, 50% at most). Program 7bSubEgdRkfRBKUqrzYGrFokzuzFt1vakRRThxQ4eMvU. The rest of the program is the allowlist's, with band_bps in place of the list.
