Skip to content

Previous releases

The contracts, launch generations and SDK that came before Hookr's current contracts, and the pools that still run on them.

On this page

Hookr's current contracts are what the rest of these docs describe. This page keeps what a builder still needs about the contracts that came before them. A pool never changes its hook, so a pool opened on those contracts keeps trading on them. Hookr opens new pools on its shared hook, never on these.

Two families are covered. The modular roots, which the app calls V2, share one registry, coordinator, router and quoter, and run five native Hook Blocks on every root; one of them adds arb recapture, and three superseded ones did too. The launch generations 3, 4 and 5 are the launchers from before the modular roots: a stepped bonding curve, then instant pools and auctions. The app calls generations 3 and 4 Legacy and generation 5 V1.

Contracts

Every Hookr contract address is read from the deployment record or the manifests the app ships; none is typed into this page. A short address on a live contract links to the explorer. The superseded roots and the launch generations are text.

Hookr's earlier contracts on Robinhood ChainCallers reach Hookr pools through Uniswap's PoolManager, which calls one of two root hooks. Both roots run the shared accounting kernel and module, and the recapture root also corrects through WTH's executor.CallersRouters, quoters and positionsUniswap v4Root hooksAccounting and recaptureModule, treasury and partnerAdmissionCoordinator librariesLinked libraries and factoryLiquidity providersTraders, routers and aggregatorsCreatorsPositionManagerUniswap v4 positionsUniversalRouterpinned 2.1.1V4Quotersimulates a swapKernelRouterV3trusted routerKernelQuoterV1trusted quoterPermit2allowancesUniswap v4 PoolManagerunlock, swap and hook callbacksHookrModularHookV6default root, flags 0x28ccHookrModularHookV6WthV5recapture rootSuperseded recapture roots3 roots, pools still tradeHookrSwapAccountingKernelV3delegatecall target of both rootsHookrModularCorrectionLibV3recapture dispatchHookrWthExecutorAdapterV1frozen recapture executorHookrNativeMechanicsBlockV2five native blocks, claim ledgerHookrTreasuryForwarderV1protocol shareWTH's recapture executorrecapture partnerHookrModuleCatalogV1admits modulesHookrStackRegistryV2one frozen stack per poolHookrMarketCoordinatorV5opens marketsReservationLibV1linked libraryTokenDeployerV3deploys the tokenInitialBuyLibV4creator buyNativeMechanicsCoordinatorLibV2admission, by delegatecallHookrReleaseCreate2FactoryV1mined the default rootHookrModularCorrectionLibV2linked, never dispatchedHookrStatefulSettlementLibV1linked into the kernel
The earlier release's contracts: callers, its two roots, the accounting kernel both delegatecall, the recapture root's arb recapture path to WTH's executor, the module, the treasury and the admission path. Select a contract for its address and connections, or follow a flow step by step.

Select a contract on the map to see its address, role and connections. Choose a flow to follow it one step at a time.

Select a box on the map to jump to that contract's address, role and connections in the list below.

Every contract, address and role (30)

Liquidity providersCaller

Add, remove and collect liquidity through Uniswap's PositionManager. Hookr has no liquidity contract of its own.

Read more: Previous releases

Uniswap v4 PoolManagerUniswap contract

0x8366a39CC670B4001A1121B8F6A443A643e40951

Uniswap v4's singleton where every Hookr pool lives. It calls the pool's root hook on each callback.

Read more: Contracts and addresses

HookrModularHookV6Live Hookr contract

0xb3cA29cF721380CEe8b8e4755F3865Ebc68Fe8cC

The default root hook every standard Hookr pool names in its pool key, with HookrSwapKernelV3 compiled in. It reads the pool's frozen stack and runs each callback body in the shared accounting kernel.

Read more: HookrModularHookV6

HookrModularHookV6WthV5Live Hookr contract

0xb914f955294799de4b891bd2EA8AF628Fa1c68CC

The recapture root, with HookrSwapKernelV5Wth compiled in and the same kernel, registry and module as the default root. It adds a recapture through WTH around each swap and refuses a swap that arrives inside a registered v3 pool's callback.

Read more: HookrModularHookV6WthV5

Superseded recapture rootsSuperseded, pools still trade

  • HookrModularHookV6WthV4 0xE5429dB8f63912E632E86733905667AaEb6ea8cC
  • HookrModularHookV6Wth, third 0xa99902a2922014bBe2Bf2dCF15742ac5104828Cc
  • HookrModularHookV6Wth, first 0xc7c516CD5546bCB2592Fe3f8aa91C2A4bA3768CC

Earlier recapture roots whose pools still trade. New recapture markets open on the recapture root.

Read more: Previous releases

Payout target

0xF4Ab4698554D5c95874986d5e956c62e5E6aB3eE

The address the forwarder pays today, as its target reads back on chain. The owner can rotate it without touching a pinned address.

Read more: Previous releases

HookrMarketCoordinatorV5Live Hookr contract

0x53A192A3fCeE94Da77916B461E0cCa2Dd6402442

Opens new-token and existing-asset markets on either root. It holds a new-token market's founding position and sets the protocol share per creator.

Read more: HookrMarketCoordinatorV5

The flows, step by step (5)

A swap on the default root

  1. A caller asks a router to swap on a Hookr pool key.
  2. The router unlocks the PoolManager and settles the swap inside that one unlock.
  3. The PoolManager calls beforeSwap on the root hook.
  4. The root reads the pool's frozen stack and checks whether the caller is the pool's trusted router or quoter.
  5. The root delegates to the accounting kernel, so all state stays in the root's own storage.
  6. The kernel calls the native block, checks what it returns against the pool's frozen caps and credits the claims.
  7. The PoolManager executes the swap at the overridden fee. Then afterSwap runs the same path for the take and the burn.

A swap on the recapture root

  1. The PoolManager calls beforeSwap on the recapture root.
  2. The root reads the executor the adapter is bound to.
  3. It asks WTH's executor whether a registered v3 pool for this pair is locked. A clean true refuses the swap, because the caller is inside that pool's callback.
  4. The same accounting kernel runs the five native Hook Blocks.
  5. The root dispatches one recapture through its linked library, before and after the swap. A failed recapture never blocks the swap.
  6. The library calls the pool's frozen recapture executor, the adapter.
  7. WTH trades the gap and pays every share in the same transaction. The creator gets 40%, a trader through the Hookr router 20%, the pool's LPs 20%, WTH 10% and Hookr 10%.

Opening a market

  1. A creator opens a new-token or existing-asset market on either root.
  2. The coordinator runs its admission library, which checks the module and the protocol share for this creator.
  3. On the new-token lane the token library deploys the token by CREATE2.
  4. The coordinator creates the pool's stack; the registry accepts stacks from no other address.
  5. The registry checks every module against the catalog.
  6. The coordinator initializes the pool and, on the new-token lane, mints the founding band.
  7. An optional creator buy runs in the same transaction.

Adding liquidity

  1. An LP mints or increases a position through Uniswap's PositionManager.
  2. An ERC-20 leg is pulled through Permit2, so the token is approved to Permit2 first.
  3. The PositionManager modifies liquidity on the PoolManager.
  4. A new-token pool refuses outside liquidity while its guard window is open. Removing liquidity is never blocked.

The protocol share

  1. Each opted-in add-on (surcharge, guard, burn, LP reward or pot) carves out the protocol share as a claim in the native block.
  2. The block's protocol recipient is the forwarder, fixed for good.
  3. Anyone can call collect, which claims the block's balance for one quote currency into the forwarder.
  4. The forwarder pays its owner-set target in the same call. If the target refuses, the funds rest on the forwarder until sweep.

Modular Roots and Their Stack

ContractWhat it isAddress
HookrModularHookV6Default root hook, flags 0x28cc0xb3cA…e8cC
HookrModularHookV6WthV5Arb recapture root: the default root plus the recapture lane0xb914…68CC
HookrModularHookV6WthV4Superseded arb recapture root, fourth0xE542…a8cC
HookrModularHookV6WthSuperseded arb recapture root, third0xa999…28Cc
HookrModularHookV6WthSuperseded arb recapture root, first0xc7c5…68CC
HookrStackRegistryV2Registry: sealed root profiles, one frozen stack per pool0x5b7f…5BA3
HookrModuleCatalogV1Catalog: admits module implementations0x6a35…19D1
HookrMarketCoordinatorV5Coordinator: opens markets on both lanes0x53A1…2442
HookrSwapAccountingKernelV3Kernel: the callback bodies every root delegates to0x5305…E060
HookrNativeMechanicsBlockV2The module that holds the five native Hook Blocks0xD700…F1ae
HookrKernelRouterV3Trusted router: swaps that carry an authenticated payer0xf0E5…66E9
HookrKernelQuoterV1Trusted quoter: simulates a swap and reverts with the result0x5Ba8…B1B5
HookrTreasuryForwarderV1Pinned protocol fee recipient with one owner-settable target0x9BB7…Af6f
HookrWthExecutorAdapterV1Adapter bound once to What The Hook's executor0x28AF…D806
What The Hook's executorTrades each arb recapture and pays the legs0xc356…5803

The libraries linked into them, and the factory the default root was mined through:

ContractLinked intoAddress
HookrNativeMechanicsCoordinatorLibV2the coordinator0xafC0…c7E2
HookrMarketCoordinatorInitialBuyLibV4the coordinator0xE12f…e382
HookrMarketCoordinatorTokenDeployerV3the coordinator0x7A28…9399
HookrMarketCoordinatorKernelReservationLibV1the coordinator0x8C2E…521d
HookrStatefulSettlementLibV1the kernel0xde7A…1706
HookrModularCorrectionLibV2the default root0xd283…79D8
HookrModularCorrectionLibV3the arb recapture root, source not verified0x1199…F99C
HookrReleaseCreate2FactoryV1the factory the default root was mined through0xBc54…ccDe

The deployer and owner of the registry, catalog, coordinator and forwarder is 0xF4Ab…B3eE. The forwarder's target was the same address when read on 2026-10-17; rotating it is the one payout change that touches no pinned address.

Launch Generations

GenerationLaunch pathsLauncherHookRouterDeployed at block
3 (Legacy)stepped bonding curve0xaAed…d6C30xd000…a0CC0x3f6E…f21b27,763,919
4 (Legacy)bonding curve, instant pool0x5Ce7…6e3B0xa0F2…28cC0x98F3…4fC533,485,079
5 (V1)instant pool, auction0xa043…e6600xe7c3…E8CC0x644a…6ca041,850,162

Generation 5 also carries a flat-fee buyback burner at 0x8Cee…FD89. Every generation sits on the same Uniswap v4 PoolManager, 0x8366…0951.

Registry Identifiers

The values registerKernel, registerModule, registerIntegration and sealRootProfile returned for the default root, which an integrator reads back rather than recomputing.

IdentifierValue
Kernel id, default root0x1be0c118b1c6520d97de31ee9f0c33069f0e715ffcdcb16c87a343752bb5be14
Kernel id, arb recapture root0xd8b6c165b82efc3b7498081f071ea4f2476e2fb9c61b2e614aba2a016f94555a
Module id0x961091565aaf4eaa4996296fda9772957e61d18d90b965111a59ded2bbb5e774
Router integration id0xc9ddc18b51d83cdafa6da985c67b4db41895dcde37b719597bdda9db6396c455
Quoter integration id0x6e5a5b6aa6d141ccaf3b9f25f60e87919fc7a724cc845d146581eb49c079a9a9
Root profile manifest hash0x74f018eb9afa65dcf38eb682397a21937b8568572c9884faa12465833bbcf213

Which Pools Use Which Contracts

ContractsPools
The default rootEvery modular pool that did not ask for arb recapture. Its five native Hook Blocks run on any quote currency. It is on Uniswap's routing allowlist, approved in September 2026.
The arb recapture rootPools opened with the five arb recapture fields set. It sealed on 2026-09-12, and its pools keep What The Hook's executor for good. Hookr's app no longer offers it.
The three superseded arb recapture rootsSix canary and rehearsal pools Hookr opened between 2026-09-10 and 2026-09-12, all HOOKR/ETH, FRONG/ETH or TEST/ETH. None was paused or withdrawn.
Launch generations 3, 4 and 5Every token launched through them, with the pool, hook and router that launch used.

A root is superseded, not retired: nothing on chain marks one, and nothing stops a swap on a pool that names one. An indexer or router that filters on one root address misses the rest.

Contract Notes

Each note is what a builder still needs from the contract's full reference page. The ABIs are at the end of this page. In the contracts, arb recapture is still named a correction (correctionExecutor, CorrectionAttemptSucceeded); these notes say arb recapture.

Hookr Modular Hook V6

HookrModularHookV6 is the default root hook, mined so address & 0x3fff == 0x28cc, and the hooks field of every pool opened on it. It inherits HookrSwapKernelV3, handles beforeSwap and afterSwap itself and forwards every other selector to the accounting kernel by DELEGATECALL, so all state stays in the root. Four immutables pin its wiring (PoolManager, registry, coordinator, accounting kernel), and the kernel's code hash is re-checked before every delegation (AccountingKernelCodeChanged).

  • beforeSwap resolves the pool's frozen stack, requires the PoolKey to match it, authenticates the caller against the pool's trusted router and quoter at their registered code hashes, and decodes hookData only for a trusted caller. An untrusted caller must send empty hookData.
  • The root's profile names no arb recapture executor, so every pool on it freezes all-zero arb recapture fields and the lane never runs. The three recapture events are declared and never emitted here.
  • Errors a caller meets: InvalidStack, InvalidHookData, InvalidPayload, ReentrantCallback, DelegateCallFailed, AccountingKernelCodeChanged, InvalidWiring.

Hookr Modular Hook V6 WTH V5

HookrModularHookV6WthV5 is the arb recapture root. It runs the same module, over the same coordinator, registry and accounting kernel as the default root, and adds one lane step and one refusal. Its kernel id is 0xd8b6c165b82efc3b7498081f071ea4f2476e2fb9c61b2e614aba2a016f94555a, its profile read sealed on 2026-09-12 with the same module set, and the profile names the adapter as its only executor. It is mined to the same 0x28cc, because the lane needs no hook permission of its own.

  • The lane runs in both phases: beforeSwap after the fee override and before the swap is priced, and afterSwap after it settles. One swap can emit two arb recaptures, told apart by the indexed phase (1 for before, 2 for after).
  • The before-swap half only runs for a swap that carries the canonical full-fill price limit for its direction. A router that sets its own sqrtPriceLimitX96 takes the after-swap half alone, with no event for the half that did not run.
  • The call to the executor sits in a try and catch. A revert there is a CorrectionAttemptFailed event and the swap settles. This is the one fail-open path in the stack.
  • The lane has no gas gate. A swap sent with a limit too small for the arb recapture runs out of gas and reverts instead of skipping.
  • Before the lane runs, beforeSwap reads wthExecutor() from the adapter (30,000 gas) and asks the executor's MEV view, checkV3PoolsMev (0x6075521e) and then checkV3PoolsMEV (0x85c30352), at 200,000 gas each. A clean true means the caller is inside a registered v3 pool's callback, closing an arbitrage, and the swap reverts MevCallbackRefused(address, address) (0xcbd9472b). A router that aggregates across venues in one transaction can meet it; drop that route.
  • correctionMaxVolumeBps and correctionMinProfitQuote are frozen on each pool and not enforced: the executor's interface takes neither. Live pools carry 5000 and 1.
  • The root and its swap kernel are verified on Blockscout as full matches, and the root's verified source carries the library it links; the library's own address is not verified, none of them is in the public contracts repository, and the executor's source is not verified.

Hookr Swap Accounting Kernel V3

HookrSwapAccountingKernelV3 holds the callback bodies the roots delegate to, so it never runs in its own storage. At 23,965 bytes it sits 611 short of the EIP-170 limit, which is why the root and the kernel are two contracts.

  • beforeInitialize accepts a pool only from the coordinator, once, and only when the PoolKey matches the frozen stack. beforeAddLiquidity runs the module's liquidity check. The other callbacks are unset and revert HookNotCalled.
  • It walks the pool's frozen modules, enforces the per-pool caps, computes the BeforeSwapDelta and the afterSwap delta, and credits claims. syncBaseFee(key) is permissionless and pushes only the pool's own frozen base fee into the PoolManager's dynamic-fee cache.
  • A trusted caller (the pool's registered router or quoter, at its registered code hash) sends an authenticated hookData envelope bound to the pool's stackHash. Any other caller sends empty hookData or the swap reverts UntrustedHookData. This is why a Universal Router swap works but skips the pot leg.
  • Events, emitted from the root's address: MarketInitialized, ModuleFeeAccrued, ModuleFeeSkipped (worth an alert), StatefulModuleAction and HookFee.

Hookr Native Mechanics Block V2

HookrNativeMechanicsBlockV2 is a stateful module and holds all five native Hook Blocks (Hookr dynamic fees, Anti-Snipe, Auto Burn, LP Rewards, Nth-buy Pot) plus the creator royalty. The kernel calls it inside the swap, and the kernel alone executes the PoolManager actions it returns.

  • Constants: MAX_TOTAL_FEE_PIPS 500,000 (50%), MAX_PROTOCOL_SHARE_BPS 5,000 (50%), MIN_POT_BUY_WEI 0.001 ether, MAX_QUOTE_DECIMALS 36, MAX_GUARD_BLOCKS 100,000, module version 2.
  • It owns the claim ledger claimable[quote][account] and totalClaimLiability[quote], backed by ERC-6909 claims on the PoolManager. claim(quote) and claimTo(quote, to) pay out and revert NothingToClaim on a zero balance or ClaimTransferFailed unless the recipient's balance rises by exactly the debit. accountingInvariant(quote) checks that the module's balance covers the liability.
  • Per-pool counters: potWei, potBuyCount, guardLpEarnedQuote, totalHookFeesWei, totalBurnedTokens, totalLpDonatedWei, totalPotPaidWei, totalProtocolShareWei and protocolShareByStream.
  • Events: HookFeesAccrued, ProtocolShareAccrued, LpRewardsDonated, JackpotHit, AutoBurn, Claimed.
  • Errors: InvalidConfig, NotKernel, StatefulKernelRequired, MaxBuyExceeded, ExactOutputBlockedDuringGuard, ExternalLiquidityBlockedDuringGuard, PartialFillUnsupportedWithInputCuts, ProtocolShareNotEnforced, InvalidPotRecipient, NothingToClaim, ClaimTransferFailed.

Hookr Module Catalog V1

HookrModuleCatalogV1 admits module implementations. A registration pins the implementation address, its runtime code hash, its config schema hash, its required hook flags, its phase mask, its exclusive group, its execution mode and four structural ceilings, and none of it can be edited. The owner can retire a module for new pools; open pools keep theirs.

  • setCanonicalStatefulModuleOnce binds the one stateful module the catalog admits, once. One catalog serves exactly one stateful module, so a new module needs a new catalog, a new registry and a new root.
  • The native module's registration fixes requiredHookFlags 10444 (0x28cc), all phases, STATEFUL_V1, maxLpFeeSurchargePips 500,000, maxSpecifiedQuoteTakeBps 4,000, maxUnspecifiedQuoteTakeBps 2,500, maxSubjectTakeBps 1,000 and callbackGasLimit 2,000,000. Every pool's own derived caps must fit inside them.
  • Retiring is retireModule; ownership moves in two steps (proposeOwner, acceptOwnership).

Hookr Stack Registry V2

HookrStackRegistryV2 admits pools. It registers kernels (root implementations) and integrations (a router, a quoter, an arb recapture executor), seals a root profile once per kernel, and freezes one StackCore per PoolId. It keeps the per-market kernel-instance lane switched off, so every pool runs on a sealed profile.

  • setCoordinatorOnce binds the one coordinator allowed to call createStack. sealRootProfile names the allowed modules and the router, quoter and executor integration ids, and it is irreversible. Pass the ids registerIntegration returned, not the integration kinds.
  • stack(poolId) is the read an interface wants: kernel, subject, quote, stack hash, the trusted router and quoter with their code hashes, and the frozen StackLimits. moduleAt(poolId, index) returns a module snapshot and its config, frozenModuleConfigHash(poolId, module) the hash re-derived on every callback, and rootProfile(kernelId) the sealed profile.
  • StackLimits holds baseLpFeePips, maxLpFeePips, the three take caps, maxTotalModuleGas, trustedRouter, trustedQuoter and the five arb recapture fields: correctionExecutor, correctionCreator, correctionMaxVolumeBps, correctionMinProfitQuote and correctionFeePolicyId.
  • Events: StackConfigured(poolId, stackHash, kernelId, subject, quote, moduleCount) and StackInitialized mark a pool's birth. Registrations, retirements and RootProfileSealed are administrative and never reach an open pool.
  • The two reverts a launcher meets in practice are ModuleCapsExceedStackLimits (the config asks for more than the pool's own limits) and ModuleConfigCapsExceedSnapshot (more than the catalog registration).

Hookr Market Coordinator V5

HookrMarketCoordinatorV5 is the only address the registry accepts stacks from. It opens both lanes, holds the founding position for the new-token lane, resolves the protocol share for the creator and exposes the treasury the admission library checks against.

  • openNewTokenMarket(args, intentId, expectedToken) deploys the token by CREATE2, opens the pool, places the whole 1,000,000,000 supply in one coordinator-held band and optionally runs the creator buy. openExistingTokenMarket(args) initializes a fresh pool for a token that already exists, with no liquidity, no band and no creator buy. collectLpFees(poolId) is permissionless and pays the founding position's fees to the market's lpFeeRecipient.
  • Constants: SUPPLY 1,000,000,000e18, MAX_INITIAL_BUY_SUBJECT 5% of supply, MAX_PROTOCOL_SHARE_BPS 5,000, MAX_MODULE_DATA_LENGTH 3,904, BAND_TICKS 207,000, DYNAMIC_FEE_FLAG 0x800000. defaultProtocolShareBps starts at 2,000.
  • The owner can pause opening (marketOpeningPaused; the owner can always open), set the default share and set or clear a per-creator tier, all at or below the ceiling. The deployed coordinator read unpaused on 2026-10-17.
  • Events: MarketCreated, ProtocolShareResolved, CreatorBuyExecuted, LpFeesCollected, CreatorTierSet, CreatorTierCleared, DefaultProtocolShareBpsSet, MarketOpeningPauseSet.
  • Reverts: NotCreator, InvalidMarketArgs, IntentAlreadyUsed, TokenSaltAlreadyUsed, InvalidPayment, UnexpectedToken, TaxedTransfer, InitialBuyAboveCap, NoFoundingPosition, ProtocolShareTierMismatch.

Hookr Native Mechanics Coordinator Lib V2

HookrNativeMechanicsCoordinatorLibV2 is the coordinator's admission and fee-routing library, run by DELEGATECALL. validateOrigin finds the one canonical native module in a market's selections and proves every admission fact: module version exactly 2 and its config schema hash, execution mode and phase mask, an on-chain code hash equal to the catalog's, validateProtocolShare(config), the module's recipient equal to coordinator.treasuryBeneficiary(), the config's share equal to coordinator.protocolShareBps(creator), and a guard window only on the new-token lane. Every external read except the coordinator's own treasuryBeneficiary() is a gas-bounded staticcall (150,000 gas) that needs a full 32-byte answer, so a target that reverts, runs long or returns short fails admission. It also routes founding-position fees at collect time: 100% of both currencies to lpFeeRecipient, with no withholding.

Hookr Kernel Router V3

HookrKernelRouterV3 is the trusted router. It settles swaps against a frozen stack and carries an authenticated payer and recipient in hookData, which is what lets the hook credit the pot to a wallet instead of a router contract.

  • exactInput(params, moduleData) and exactOutput(params, moduleData). A native input needs msg.value equal to the input (amountInMaximum on exactOutput) and refunds the rest; an ERC-20 input needs msg.value == 0 and an allowance to this router. moduleData is at most 3,904 bytes.
  • On any pool with input cuts, set sqrtPriceLimitX96 to the canonical full-fill limit (MIN_SQRT_PRICE_LIMIT for zeroForOne, MAX_SQRT_PRICE_LIMIT otherwise) or the swap reverts PartialFillUnsupportedWithInputCuts.
  • Integration version 3; the creator buy requires it. Constructed after setCoordinatorOnce, with the coordinator's code hash pinned. Emits SwapExecuted(poolId, stackHash, payer) for swaps routed through it; a Universal Router swap emits nothing here.
  • Errors include InputDebitMismatch and SettlementMismatch for a fee-on-transfer quote.

Hookr Kernel Quoter V1

HookrKernelQuoterV1 is the trusted quoter. quote(params, moduleData) unlocks the PoolManager, runs the same hook callbacks, the same module walk and the same fee override a real swap would, and reverts with the result, so it is not a view function: call it with eth_call. Being the pool's registered quoter also means the pot leg is simulated. A quote from Uniswap's V4Quoter sees the same pool without the pot cut, which is what a Universal Router swap pays. Integration version 1.

Hookr Treasury Forwarder V1

HookrTreasuryForwarderV1 is the one address every market pins as its protocol recipient, with a rotatable payout behind it. It holds no protocol authority: it cannot open markets, change tiers, pause anything or touch a pool.

  • collect(quote) is permissionless: it claims the module's accrual into the forwarder and forwards it to target in the same call, and returns zero when nothing is owed. If the target rejects the payment, ForwardDeferred is emitted and the funds rest on the forwarder. sweep(token) then pushes them, and reverts if the target still refuses.
  • Owner functions: setTarget, setTargetAndCollect(newTarget, quotes) (rotate and drain atomically, best sent through a private relay when the old target is believed compromised) and setNativeBlock. The target cannot be the forwarder, the bound module or the PoolManager.
  • Events: Collected, ForwardDeferred, Swept, TargetSet, NativeBlockSet, and the ownership events.

Hookr Market Coordinator Token Deployer V3

HookrMarketCoordinatorTokenDeployerV3 is a linked library that deploys a new-token market's fixed-supply HookrTokenV61 with CREATE2 and derives its founding band. It runs by DELEGATECALL, so the token is deployed from the coordinator's address and the address depends on the coordinator, not the library. initCodeHash lets anyone predict the address before the launch; the coordinator's previewNewTokenAddress(args, intentId) returns it directly.

Hookr Market Coordinator Initial Buy Lib V4

HookrMarketCoordinatorInitialBuyLibV4 validates and executes the creator's same-transaction buy. A zero quoteAmountIn disables the buy. validate reverts InvalidInitialBuy for an amount above int128 max, a zero minimum, a passed deadline, moduleData over 3,904 bytes, or a router that is not the pool's integration version 3 with the coordinator's current code hash. executeAndEmit calls the router's exactInputInitialBuy, emits CreatorBuyExecuted and reverts InitialBuyInputMismatch unless the buy consumes exactly the requested quote. The buy is available on every quote currency.

Hookr WTH Executor Adapter V1

HookrWthExecutorAdapterV1 stands between a recapture root and a partner's executor. It names one executor, once, and holds nothing. Four adapters at this name are deployed, one per recapture root, each bound to the executor What The Hook ran at the time; resolve a root's adapter from the registry (rootProfile(kernelId).correctionExecutorIntegrationId, then integration(id).implementation) rather than from the name.

  • setExecutorOnce(executor) (0x76e5397a) is owner-only and already spent: a second call reverts ExecutorAlreadySet() (0xa1002dd1), and any other caller reverts NotOwner() (0x30cd7471) first. wthExecutor() (0x477a9d7c) returns 0xc356…5803 on the current adapter.
  • Views: feePolicyId() (0xc893dd97), routeAdmissionOpen() (0xc93b3f0f, true), executionBlockNumber(), integrationKind(), integrationFamilyId() and integrationVersion() (1). executionBlockNumber() returns this chain's own height through the ArbSys precompile, not block.number.
  • It forwards three arguments to the executor (the triggering pool, the rebate recipient and the profit split). The swap's direction and size, the pool's frozen volume and profit fields and the plan are dropped at this boundary.
  • The adapter and the root are verified on Blockscout as full matches; the executor is not.

How Pools on the Modular Roots Work

The notes below cover what the contract notes do not: how the pieces behave together.

Fee Model

The protocol never takes a slice of the base LP fee. It takes a share from inside each opted-in add-on, 20% at the deployed default and never more than 50%, and the trader pays exactly the amounts the pool was configured with. A pool that opts into nothing but a base fee generates no protocol revenue.

Config fieldStreamWho pays itWhere it goes at the 20% default
baseFeePipsBase LP feeevery swap100% to in-range LPs
maxFeePips above baseHookr dynamic fee surchargeswaps that move price80% to in-range LPs, 20% to the protocol
snipeTaxPipsGuard snipe taxbuys during the guard window80% to the creator, 20% to the protocol
lpBpsLP Rewards cutexact-input buys80% donated to in-range LPs, 20% to the protocol
potBpsNth-buy Pot cutexact-input buys through a trusted caller80% into the pot, 20% to the protocol
burnBpsAuto Burnexact-input buys80% of the withheld subject burned, 20% taken in quote by the protocol
royaltyBpsCreator royaltyshare of the LP Rewards and pot cutsto royaltyTo, computed after the protocol's share

The share s is resolved once when the market opens and frozen into the pool's config; in general the split is 1-s to the LP side and s to the protocol. The trader's total fee rate is base + dyn + snipe in every quadrant, so the split moves value between LPs and the protocol and never adds to the price.

  • Exact-input buy. Every leg lands on the quote input in beforeSwap. The protocol's pips leg is pDyn + pSnipe + burnP * 100 with pDyn = dyn * s / 10_000, and the cut B = x * (lpBps + activePot) / 10_000 is split by post-share weights into LP donation, pot and royalty, with the protocol taking the residual.
  • Exact-output buy. No LP Rewards, pot or burn leg. The dynamic fee uses its ceiling maxFeePips - base, because the input is unknown when the fee is set, and afterSwap takes the protocol's slice in quote.
  • Exact-input sell. The dynamic fee applies and the protocol's slice is taken from the quote output in afterSwap. The value is carried from beforeSwap to afterSwap in transient storage.
  • Exact-output sell. Exempt. The unspecified currency is the subject, which the protocol never holds, so LPs keep the whole surcharge.
  • Burn slice. The protocol never holds subject tokens, so its slice of a burn is taken in quote: burnP = burnBps * s / 10_000, charged as that many bps of the gross quote input.
  • Rounding. Every slice floors the protocol's rate, so it is never above s. The protocol absorbs the shared rounding dust.
  • Partial fills. A pool with a specified-leg take (a dynamic fee, guard, burn, LP reward, or a pot through a trusted caller) requires the canonical full-fill price limit and reverts PartialFillUnsupportedWithInputCuts otherwise. A pool with only a base fee accepts partial fills.
  • Creator buy. At most 5% of the 1,000,000,000 supply measured after any burn (InitialBuyAboveCap), on any quote. It pays the snipe tax and respects the per-block cap. An ERC-20 quote needs the creator's approval to the router first.

Guard Window

Anti-Snipe's launch window, available only on the new-token lane. Requesting it on an existing-asset market reverts GuardRequiresLockedFoundingPosition. It is open while block.number < guardEndBlock, and four limits apply:

LimitBehavior
Per-block buy capmaxBuyQuoteAmount bounds the quote spent on buys in one block for the pool, not per address (MaxBuyExceeded). Zero disables the cap.
Snipe taxsnipeTaxPips is added to the LP fee on buys, split 80% creator and 20% protocol at the default share.
Exact-output buysBlocked (ExactOutputBlockedDuringGuard).
Outside liquidityBlocked for any sender but the coordinator (ExternalLiquidityBlockedDuringGuard).

The founding position is the only liquidity while the window is open, so the creator's lpFeeRecipient receives the LP part of every guard-window fee, and a creator who buys their own launch recovers most of the tax; the 5% creator-buy cap bounds it. Bounds: guardEndBlock after the opening block and at most 100,000 blocks ahead, baseFeePips + snipeTaxPips <= 500_000, lockedLiquidityProvider the coordinator while the guard is on, and zero fields when it is off. Every limit stops at guardEndBlock. guardLpEarnedQuote(poolId) is an informational counter that separates guard-window earnings from later ones.

Native Mechanics

The five blocks live in one module because splitting them would change the fee rounding: one hook cut is computed across LP Rewards and the pot, the royalty is removed once, and the protocol absorbs the shared residue. Each block is switched off by setting its rate to zero.

  • Hookr dynamic fees. reserveIn is the in-range reserve on the input side, ratio = min(amountIn * surgeSens * 1e6 / reserveIn, 1e6) and surge = (maxFeePips - baseFeePips) * ratio / 1e6. surgeSens is capped at 10 and is zero exactly when maxFeePips == baseFeePips. Sells pay it too.
  • Auto Burn. On an exact-input buy, burnBps of the subject output is withheld and the pool's share is sent to 0x000000000000000000000000000000000000dEaD. The subject must permit that transfer, or every buy reverts and the pool fails closed. Burning a tokenized stock or a stablecoin destroys real value.
  • LP Rewards. lpBps of the gross quote input is donated to in-range liquidity inside the same swap, so a position that leaves range stops receiving it at once.
  • Nth-buy Pot. potBps of the gross quote input fills the pot, and the buy that makes the counter divisible by potEveryNBuys takes all of it. A buy counts only at or above potMinBuyWei (0.001 ETH natively, at least 10 ** (decimals - 3) for an ERC-20 quote), and the counter advances at most once per pool per block. The pot needs an authenticated recipient, which only the registered router or quoter can supply; a swap from any other caller pays no pot cut and cannot win. The pot is not random: potBuyCount is public, so expect it to be raced.
  • Royalty. royaltyBps is a share of the LP Rewards and pot cuts, computed after the protocol's share and credited to royaltyTo. If it is zero, royaltyTo must be zero.

Pool Lifecycle

  1. Configure. The caller assembles MarketParams (quote, tick spacing, opening price, kernelId, module selections, StackLimits) with exactly one native module config. limits.baseLpFeePips must equal the config's baseFeePips.
  2. Admit. The coordinator calls the admission library, which proves the facts listed under the coordinator library above.
  3. Freeze. createStack derives per-pool caps, checks them against the catalog registration and stores the StackCore. From here nothing in the registry, catalog or coordinator can change the pool.
  4. Initialize. The coordinator initializes the pool with fee = 0x800000 and the root as hooks. beforeInitialize accepts only the coordinator, only once, only against the frozen stack, and emits MarketInitialized.
  5. Liquidity. The new-token lane places the whole supply in one token-only band whose quote-side edge sits on the opening price, owned by the coordinator with no removal path. The existing-asset lane adds nothing; LPs use the ordinary v4 PositionManager.
  6. Creator buy. New-token lane only, once, after the band is seeded.
  7. Trade and collect. Founding-position fees wait for collectLpFees, protocol shares for collect(quote) on the forwarder, and pot wins and royalties for claim on the module.

A pool has no expiry, migration or shutdown. The two lanes:

New-token laneExisting-asset lane
Tokendeployed by the coordinator, fixed supplyany deployed token
Founding positioncreated, never removablenone; lpFeeRecipient must be zero
Guard windowallowed, up to 100,000 blocksrejected
Creator buyallowed, at most 5% of supplyrejected
Who may openthe creator, expectedCreator == msg.senderanyone while opening is not paused

openExistingTokenMarket performs no ownership check on the subject, and market.creator records only who opened the pool. Never present it as token ownership or as the token team's endorsement.

Protocol Share and Treasury

protocolShareBps(creator) returns the creator's tier if the owner set one and defaultProtocolShareBps otherwise. Both are bounded by 5,000 on write (ProtocolShareAboveCeiling). Tiers are keyed on the address that calls the open function, so a tier granted to a launcher contract applies to every market it opens, and a tier on a wallet covers only that wallet.

Admission reverts ProtocolShareTierMismatch(expected, actual) unless the config carries exactly that value, and the hook re-derives the config hash on every swap and liquidity add, so a later change to the default or to a tier cannot reach an open pool. The module's protocolRecipient is immutable and the coordinator's treasury is constructor-only, and they must be equal. The forwarder is the pinned address, so rotating the payout destination never de-admits a market.

The share is never taken from the base LP fee, never in subject tokens and never on top of what the trader agreed to pay, and it is zero on a pool that runs on its base fee alone.

Immutability and Ownership

Frozen per pool at creation: the base LP fee, the dynamic fee ceiling and sensitivity, the guard window and its parameters, the burn, LP reward, pot and royalty rates, the royalty recipient, the pot floor and interval, the protocol share and recipient, and the trusted router and quoter. The founding-position fee recipient is written once into the coordinator's market record with no setter.

Four owner slots exist, each under a two-step transfer (proposeOwner, acceptOwnership): the coordinator, registry, catalog and forwarder. One address holds all four.

ContractOwner functionEffectReaches a live pool?
CoordinatorsetMarketOpeningPausedWhile paused only the owner may open marketsNo
CoordinatorsetDefaultProtocolShareBps, setCreatorTier, clearCreatorTierChanges the share future markets resolve toNo
RegistryregisterIntegration, retireIntegration, registerKernel, retireKernelAdmits or retires a router, quoter, executor or root for future stacksNo
RegistrysetCoordinatorOnce, sealRootProfileOne-shot bindsNo
CatalogregisterModule, retireModule, setCanonicalStatefulModuleOnceAdmits or retires a module for future stacksNo
ForwardersetTarget, setTargetAndCollect, setNativeBlockRotates where collected shares are paidNo

Nobody can change a pool that is open, pause, freeze or blacklist a pool, a swap or a trader, remove the founding position, take a share of the base LP fee, raise MAX_PROTOCOL_SHARE_BPS (a module constant with no setter), upgrade anything (there are no proxies), or move funds owed to somebody else. Anyone can call openExistingTokenMarket (while opening is not paused), collectLpFees, collect, sweep, claim and claimTo; none of them lets the caller redirect value.

RWA and ERC20 Quotes

Every Hook Block works on every quote currency, and the claim ledger is keyed by quote, so an ETH-quoted and a USDG-quoted pool share the module without their balances crossing.

  • Payment. Opening a market with an ERC-20 quote needs msg.value == 0 (InvalidPayment). The coordinator pulls no quote itself; only the creator buy moves quote at launch, and the router pulls it from the creator's approval.
  • Pot floor. An ERC-20 quote needs potMinBuyWei >= 10 ** (decimals - 3), checked with a bounded decimals() read that rejects fewer than 3 or more than 36 decimals. USDG has six, so its floor is 1,000 base units.
  • Transfers. The coordinator and the module compare balances before and after every ERC-20 movement (TaxedTransfer, ClaimTransferFailed), so a fee-on-transfer or rebasing quote does not work.
  • Robinhood tokenized stocks. A stock token on chain 4663 is a BeaconProxy over a shared Stock implementation. Pause and blocklist state lives in one chain-wide contract at 0xe10b6f6B275de231345c20D14Ab812db62151b00, so a pause halts every pool holding the token, and a blocked hook or router stops its pools. A privileged role can burn a holder's balance (adminBurn), and the ERC-8056 uiMultiplier changes what an interface shows, not the pool math.

Combinations that need a warning in any interface: Auto Burn with a stock, a stablecoin or any subject the creator does not control; any pool holding a stock on either side; pot or LP rewards with a stock as the quote. The contract rejects only a subject equal to its quote and a guard window on the existing-asset lane.

Config Schema and Limits

One HookrNativeMechanicsBlockV2.Config is frozen per pool: twenty fields, 640 ABI-encoded bytes, rejected if it does not re-encode to itself. In order: poolId, kernel, subject, quote, lockedLiquidityProvider, guardEndBlock (uint40), baseFeePips, maxFeePips, snipeTaxPips (uint24), surgeSens, burnBps, lpBps, potBps, royaltyBps (uint16), potEveryNBuys (uint32), maxBuyQuoteAmount, potMinBuyWei (uint96), royaltyTo, protocolRecipient and protocolShareBps (uint24). protocolShareBps is basis points of each add-on, never a flat rate on the quote leg, which is why the schema hash is part of admission.

CheckBound
FeesbaseFeePips <= maxFeePips <= 500_000 and baseFeePips + snipeTaxPips <= 500_000
Dynamic feesurgeSens <= 10, zero exactly when maxFeePips == baseFeePips
CutsburnBps + lpBps + potBps <= 1_000 and royaltyBps <= 1_000
RoyaltyroyaltyBps != 0 needs royaltyTo != 0 and lpBps + potBps != 0; royaltyBps == 0 needs royaltyTo == 0
PotpotBps != 0 needs 2 <= potEveryNBuys <= 100_000 and a pot floor; potBps == 0 needs both zero
GuardguardEndBlock == 0 needs lockedLiquidityProvider, snipeTaxPips and maxBuyQuoteAmount zero; otherwise lockedLiquidityProvider is the coordinator
ShareprotocolShareBps <= 5_000 and equal to coordinator.protocolShareBps(creator)
Quotesubject != quote; tick spacing positive and at most type(int16).max

Other constants: DYNAMIC_FEE_FLAG 0x800000 (the required PoolKey.fee), MAX_MODULE_DATA_LENGTH 3,904 and MAX_CONFIG_BYTES 4,096.

Hook Block Combinations

On the new-token lane every Hook Block is allowed: the fee is the launcher's standard 0.30% and the guard, creator buy, burn, LP Rewards, pot and royalty all apply. On the existing-asset lane the creator picks the base fee, and the guard and the creator buy are rejected (see the lane table under Pool Lifecycle). Every block runs on every quote currency, because the module keys its cut ledger by quote. What differs from pair to pair is the warning it earns:

Subject \ QuoteETHHOOKRUSDGTokenized stockOther ERC-20
A new tokenOKOKOKOK, warn pause registryOK
An existing ERC-20OK, warn Auto Burn if the token is not yourssamesamesame, plus warn pause registrysame
HOOKROK, burn deflates HOOKRBlock, subject equals quoteOKOK, warn pause registryOK
A tokenized stockOK, warn Auto Burn destroys shares and warn pause registrysamesameOK, both warningssame
USDG or another stablecoinOK, warn Auto Burn destroys dollarssameBlock, subject equals quoteOK, warn pause registrysame

The recommended settings were: for existing pairs, the 0.30% tier with dynamic fees on and no burn on a stock or stablecoin subject; for new tokens, the guard on with a 25% to 45% snipe tax and a per-block max buy, burn 2% to 3%, LP Rewards 1% to 2%, royalty 5% to 10% of the cuts and a creator buy at or under 5% of supply; for stock or stable pairs, the base fee plus the dynamic fee only. Only swaps through the Hookr router pay into the pot.

Hook Permissions

Every modular root answers REQUIRED_FLAGS 10444, 0x28cc: beforeInitialize, beforeAddLiquidity, beforeSwap, afterSwap, beforeSwapReturnDelta and afterSwapReturnDelta. Removing liquidity is never hooked, so no Hook Block can trap an LP position. The arb recapture lane needs no flag of its own, so the recapture roots carry the same word. The registry re-derives a root's flags from its address and refuses a registration whose declared flags disagree (InvalidHookFlags, InvalidHookFlagDependencies).

  • beforeInitialize accepts initialization only from the coordinator, once per PoolId, against the frozen stack.
  • beforeAddLiquidity runs the guard-window lock.
  • beforeSwap sets the LP fee override and takes the quote-side cuts as a BeforeSwapDelta; afterSwap takes the unspecified-leg protocol share and the burn.
  • Hooks.Permissions spells the delta flags beforeSwapReturnDelta and afterSwapReturnDelta; Uniswap's hooklist schema spells them beforeSwapReturnsDelta and afterSwapReturnsDelta.
  • Every pool sets PoolKey.fee = 0x800000, the dynamic-fee flag, so fee says nothing about the real cost. Read the frozen base fee with registry.stack(poolId).limits.baseLpFeePips, or push it into the PoolManager's cache with syncBaseFee(key) at the root's address. MAX_TOTAL_FEE_PIPS is 500,000 (50%).

Allowlisting is per hook address and carries over to nothing.

Events for Indexers

Six value streams run through a pool on the default root and must not be summed. A recapture pool has a seventh.

StreamSource
Base LP feePoolManager fee growth
Hookr dynamic fee and snipe surcharge, LP partPoolManager fee growth, indistinguishable from the base fee on chain
Protocol shareProtocolShareAccrued(poolId, stream, amount), with stream one of Surcharge, Guard, LpReward, Pot, Burn
LP Rewards donationLpRewardsDonated, which also lands as fee growth
PotHookFeesAccrued for inflow, JackpotHit for the payout; balance in potWei(poolId)
BurnAutoBurn, in subject units
Arb recapture profitCorrectionAttemptSucceeded(poolId, phase, planDigest, realizedProfitQuote), gross, on a recapture root only

Counters are cheaper than replaying events: potWei, potBuyCount, guardLpEarnedQuote, totalHookFeesWei, totalBurnedTokens, totalLpDonatedWei, totalPotPaidWei, totalProtocolShareWei and protocolShareByStream. Protocol revenue received is Collected plus Swept on the forwarder, not ProtocolShareAccrued.

  • Coordinator: MarketCreated (the origin tells the two lanes apart), ProtocolShareResolved, CreatorBuyExecuted, LpFeesCollected and the owner events.
  • Accounting kernel, emitted from the root's address: MarketInitialized, ModuleFeeAccrued, ModuleFeeSkipped, StatefulModuleAction, HookFee.
  • Router: SwapExecuted, only for swaps routed through it.
  • Registry: StackConfigured and StackInitialized.
  • Recapture roots, from the root a pool names: CorrectionAttemptSucceeded (0x062af9b1a8b2f073cef7ee04bab7c582cf3aca90852ec46c3481a07b715531dd), CorrectionAttemptFailed (0xcbe0d1d2d8c523a1845d8ff0549db2778776b3a7e4932ac1dcc30354199fea99) and CorrectionAttemptSkipped (0xddf7f687f86d810a6d78924e7e77035c06caa1feb864c0f674fb55bb893c3425). Only the first carries value. De-duplicate by the indexed phase, never by transaction hash, which halves the measurement.

Do not parse a pool's configuration out of events. Read registry.stack(poolId) and registry.moduleAt(poolId, 0), then decode the config; it is frozen, so one read at any block holds for the pool's life.

Claims and Fee Collection

Four balances build up around a pool, and each is collected differently. A pool on a recapture root adds a fifth stream, paid inside the swap.

BalanceHow it is collectedWho can call
Founding-position feescoordinator.collectLpFees(poolId) pays both currencies to lpFeeRecipient; reverts NoFoundingPosition on an existing-asset marketanyone
Pot wins and royaltiesclaimable(quote, account) to read, claim(quote) or claimTo(quote, to) to pay; one balance per quote across every pool, zero reverts NothingToClaimthe account owed
Protocol shareforwarder.collect(quote), then sweep(token) if the target refusedanyone
LP Rewards donationsnothing to collect; they are fee growth, collected with a position's fees through PositionManagerthe position owner
Arb recapture payoutspaid by the executor inside the swap that realised the profit; the LP part arrives as fee growthnothing to claim

Claimed covers a pot win, a royalty and the forwarder's own collection; tell them apart by account. Keep accrued and collected as separate numbers: accrued is what the pool owes, collected is what reached an address. A read that fails is not a zero balance.

Swapping, Quoting and Liquidity

  • Universal Router. Send empty hookData. Every Hook Block applies except the pot leg, which needs the authenticated recipient only the registered router or quoter can supply. An ERC-20 input is pulled through Permit2.
  • Hookr router. exactInput and exactOutput carry the payer and recipient. An ERC-20 input needs a plain approval to the router.
  • Price limit. A pool with any input cut on an exact-input buy needs the canonical full-fill limit. Aggregators used to setting their own limit must use amountOutMinimum instead.
  • Quote. The registered quoter runs the real callbacks and reverts with the result.
  • Liquidity. Mint through the ordinary PositionManager with empty hookData (an oversized payload reverts HookDataTooLarge). A new-token pool refuses any sender but the coordinator while its guard window is open. Removing liquidity is never hooked. A narrow position earns more of the dynamic fee and leaves range faster.

Opening a Market

New-token market. Build the Config (see Config Schema and Limits), ask the coordinator for the token address with previewNewTokenAddress(args, intentId) so the config and PoolKey can name it, derive the key with poolKeyFor(subject, market) (it sorts the currencies, sets fee to 0x800000 and names the root), assemble MarketParams (subjectAmount equal to SUPPLY, quoteAmount zero, lpFeeRecipient neither zero nor the coordinator, sqrtPriceX96 exactly on a usable tick, the registered router and quoter as trustedRouter and trustedQuoter), add an optional creator buy, and send openNewTokenMarket{value: quoteAmountIn}(args, intentId, subject). msg.value equals the creator buy for a native quote and zero for an ERC-20 quote. In one transaction the coordinator deploys the token, freezes the stack, initializes the pool, seeds the band, runs the buy and emits MarketCreated, ProtocolShareResolved and CreatorBuyExecuted.

Existing-asset market. The creator chooses the base fee (500, 3000 and 10000 pips are the usual tiers), with baseLpFeePips plus the dynamic fee headroom plus the snipe tax inside 500,000. The guard must be off, so lockedLiquidityProvider, snipeTaxPips and maxBuyQuoteAmount are zero. Send openExistingTokenMarket(ExistingTokenArgs({ subject, market })) with msg.value zero; the pool is tradeable the moment someone mints a position. Before opening one, check that the subject permits a transfer to 0x…dEaD if Auto Burn is on, what a tokenized stock's pause registry does to the pool, and that market.creator proves nothing about ownership.

Launchers

A launcher is a contract that opens modular markets for its own users, and it is the creator of every market it opens: openNewTokenMarket requires args.expectedCreator == msg.sender, and openExistingTokenMarket records msg.sender.

  • Its tier applies to every market it opens. protocolShareBps(creator) resolves on the calling address, so a tier granted to a launcher applies to every one of its users until it is cleared. Tiers are owner-set on the coordinator, with no application flow in the contracts. Read your rate in the same transaction you launch in, because the config must match it exactly or admission reverts ProtocolShareTierMismatch. A tier can be zero.
  • lpFeeRecipient is where the founding fees go. Set it to a user's address or to a splitter you deploy per launch. It is frozen at creation, and the coordinator only ever pays the one address named there.
  • intentId is scoped to the creator. launchedByIntent[creator][intentId] guards replay for one creator address, which for a contract launcher is one namespace shared by all its users, so derive the id from something user-specific.
  • Predict the token. previewNewTokenAddress(args, intentId) is a view. Pass the same address as expectedToken to assert it; a mismatch reverts UnexpectedToken(expected, actual).
  • Size a creator buy. The 5% cap, maxInitialBuySubject(), is on the subject the creator receives, not on the quote spent. Compute the quote amount from the opening price and the band, quote it through the Hookr quoter, then check it against maxBuyQuoteAmount for its block. The buy must consume its whole input. A buy that breaks either bound reverts the entire launch, token deployment included.
  • The opening price is not free. The app's launcher opens every new-token market at a fixed valuation of 2.5 ETH. Against native ETH the usable tick nearest it is 198,060 for an 18-decimal subject at tick spacing 60. For USDG or HOOKR the launcher converts 2.5 ETH into raw units from a reference pool's spot price (the WETH/USDG 0.01% Uniswap v3 pool, or the hookless HOOKR/ETH v4 pool) and lands on the nearest usable tick. The coordinator itself only takes a sqrtPriceX96.
RevertMeaning
ProtocolShareTierMismatch(expected, actual)The tier changed between reading it and launching. Re-read it and rebuild the config.
MarketOpeningPaused(caller)Opening is owner-restricted right now.
IntentAlreadyUsed(creator, intentId, subject)The intent namespace collided.
TokenSaltAlreadyUsed(create2Salt, subject)The same launch arguments were already used. Vary deploymentSalt.
InvalidMarketArgsUsually the opening price is not exactly on a usable tick.
InitialBuyAboveCap(subjectOut, cap)The creator buy would deliver more than 5% of supply.
MaxBuyExceeded(attempted, max)The creator buy breaks the guard's per-block cap.
NativeMechanicsModuleRequiredThe selections contain no native mechanics module.
TaxedTransferA token moved by a different amount than the coordinator asked for. A fee-on-transfer ERC-20 quote fails in the router instead (InputDebitMismatch, SettlementMismatch) or at claim time (ClaimTransferFailed).

Before mainnet: read your own protocolShareBps in the launch transaction, set limits.baseLpFeePips equal to the config's baseFeePips, set limits.trustedRouter and limits.trustedQuoter to the registered router and quoter (or the registry rejects the stack with IntegrationOutsideRootProfile), put the opening price exactly on a usable tick, approve the router for an ERC-20 quote if the launch includes a creator buy, set potMinBuyWei to at least 10 ** (decimals - 3) for an ERC-20 quote with a pot, and test one buy against a burn-enabled pool on a fork if the subject is not a token you deployed.

Security Notes

Invariants the contracts hold, each enforced at a named place:

  • Admission: one canonical module version (exactly 2); the protocol share is proved at admission, not declared; the recipient equals the coordinator's treasury; the pair is re-checked on every swap and liquidity add (ProtocolShareNotEnforced); every admission read fails closed; a guard window implies a locked founding position.
  • Configuration: the config is byte-exact; its hash is re-derived on every callback and compared with the registry's frozen hash; per-pool caps sit inside the catalog registration; the base fee agrees across both structures.
  • Runtime: only the pool's own kernel may call the module; a stateful module never runs as a stateless one; input cuts require the canonical full-fill limit; the pot needs the authenticated router path and its counter advances once per pool per block; a trusted integration's code cannot change under a pool (TrustedIntegrationCodeChanged); the protocol never holds subject tokens; the base LP fee carries no protocol share.
  • Accounting: claims are quote-isolated and independently solvent; a claim pays exactly what the pool delivers; ERC-20 movements are balance-checked on both sides; the trader pays exactly the configured amount.
  • Lifecycle: a pool is initialized once, by the coordinator, against a stack that already exists; owner changes never reach an open pool; one-shot binds stay burned; removing liquidity is never hooked.

Trust assumptions and failure direction. The system relies on the PoolManager, on the quote and subject tokens (a token that pauses, blocklists or admin-burns can halt or distort a pool, and Hookr fails closed instead of routing around it), on the owner to set tiers sensibly and to keep the forwarder's target safe, and on the deployer's release sequence, where a wrong one-shot bind is unrecoverable. On the recapture roots it also relies on the partner executor. Every failure direction is a revert except one: the arb recapture lane is fail-open on purpose, so a failing or declining executor is a skipped arb recapture and an event. On the arb recapture root a swap with too little gas for the lane reverts; the recapture roots before it skipped the lane below a gas floor. Ordinary MEV is untouched on the default root; the recapture root refuses only a swap closing an arbitrage inside a registered v3 pool's callback.

Known limitations.

  • The protocol share ceiling can only go down, and only by deploying a new module, catalog, registry, coordinator and root, because the binds are one-shot.
  • Registration caps are permanent; sizing them too low forecloses configurations, too high widens the reviewed surface.
  • There is no per-pool admin: no kill switch, fee override, parameter change or founding-position removal, even in an emergency. A misconfigured pool stays misconfigured.
  • A creator who buys their own launch through the guard recovers most of the tax; the 5% cap bounds it.
  • Exact-output sells are free of protocol take, and a base-fee-only pool earns the protocol nothing.
  • The burn slice is computed from gross quote input, not by converting the withheld subject at the execution price.
  • An exact-output swap pays the full dynamic fee ceiling, not a size-scaled fee.
  • The pot leg depends on the router: two traders doing the same swap through different routers pay different amounts.
  • Partial fills are unavailable on most pools, and the guard is per block, not per address.
  • Tokenized stocks bring external control: a pause, a blocklist and adminBurn.
  • The contracts are deployed and unaudited. The evidence is source review, a full run of the deployment and trading path against a fork, and reads against the live contracts, plus two canary pools the deployer opened and traded.

Verification

The 16 contracts of the default root's deployment record hold verified source on Blockscout as full matches (the explorer recompiles the source and gets the deployed bytes back, CBOR trailer included), and Sourcify holds the same 16 addresses on chain 4663 as exact matches. The build is Solidity v0.8.26+commit.8a97fa7a through via-IR, the optimizer at 200 runs, evm_version = cancun, bytecode_hash = ipfs and CBOR metadata appended, with the contract repository's ten remappings written out because the metadata records them. The source commit behind the deployed code is 8db7fc940938f811f508ba9cb0c8f2d3f24c9a25, published with the deployment record as deployments/robinhood-4663.v2.json in the public contracts repository.

To rebuild one, run git submodule update --init --recursive, forge build and cast code <address>, and compare out/<file>.sol/<Contract>.json. For a contract that links libraries, write the library addresses into the placeholders deployedBytecode.linkReferences locates, and zero the spans in deployedBytecode.immutableReferences on both sides. The kernel is 23,965 bytes of runtime, the registry 23,307 and the coordinator 22,865. Four of them, the kernel reservation, stateful settlement and correction V2 libraries and the CREATE2 factory, predate the rest and carry the metadata of a build that embeds source text, so their trailers need use_literal_content = true; their code is identical either way. The single immutable in each of the three libraries is its own address, which is how a library refuses a direct call.

Arb recapture on the recapture roots

Arb recapture keeps captured price-gap value with pool participants instead of external MEV bots. The arb recapture root runs it, and so did the three superseded roots. A partner contract does the trading; Hookr's part is an adapter, a set of frozen fields on the pool, and a kernel that calls the partner and cannot be broken by it. Powered by What The Hook, or WTH (whatthehook.io): the executor that prices each arb recapture, trades it and pays the proceeds belongs to WTH, not to Hookr.

How the Lane Works

An arb recapture is one bounded call from the swap kernel to the partner executor while the swap is in flight. The executor reads this pool's price and a venue it has registered for the pair, trades the difference if it is worth trading, and reports a gross realizedProfitQuote. The executor decides whether and how much to trade; Hookr decides nothing about the trade.

  • Two phases. beforeSwap catches a gap that was open when the transaction arrived, afterSwap one the trader's own order opened. The executor is not told which phase it is in.
  • The reference venue. The executor keeps a registry of venues per pair, read through getTokenPools(address,address) (0xeb1af5d2) and registeredPools(bytes32) (0xea509a64). For HOOKR against ETH it holds one hookless v4 pool at fee 2,500 and tick spacing 25. A pair with no venue has nothing to trade against, and WTH's keeper registers a pair's counter-venues after a pool opens, so a new token starts with the lane on and nothing for it to do.
  • One quote. The registry admits an executor only on a native-ETH pool with currency0 the quote and currency1 the subject. A pool quoted in USDG, HOOKR or a tokenized stock cannot carry the lane on any root.
  • Fail-open. A revert or a decline is a CorrectionAttemptFailed or CorrectionAttemptSkipped event and the swap settles. reasonHash is the keccak256 of the revert data. Neither is a failed swap, and neither should be shown to a trader as one.
  • Refusal. The arb recapture root alone refuses a swap closing an arbitrage inside a registered v3 pool's callback; see the contract note above. The refusal is in the hook and not the adapter because the adapter sits inside the kernel's try and catch, where a revert would be swallowed.

Where the Profit Goes

Every pool on the arb recapture root freezes the same waterfall, correctionFeePolicyId 0xd2653e091cb7002585fd8b1192b58f11b9c951061dc797123e37d5e2ccb45cef. The route only decides whether a trader is paid.

RecipientThrough the Hookr routerAny other route
The pool's correctionCreator40%40%
The authenticated trader20%nothing
The triggering pool's in-range LPs20%40%
WTH10%10%
Hookr10%10%

Only a swap through the Hookr router carries an authenticated trader; on any other route the trader's share goes to the triggering pool's LPs. The executor pays every leg inside the swap that realised the profit, and the LP leg arrives as fee growth.

The root's CorrectionAttemptSucceeded carries only the gross. The executor emits the split in the same transaction:

event ArbitrageProfitSplit(
    address indexed profitCurrency,
    address indexed trader,
    address indexed creator,
    uint256 totalProfit,
    uint256 traderAmount,
    uint256 creatorAmount,
    uint256 triggerPoolAmount,
    uint256 wthProtocolAmount,
    uint256 hookrProtocolAmount
);

It carries no pool id, so attribute one split to one pool by pairing it with the root's CorrectionAttemptSucceeded from the same transaction. A transaction where the two lists do not pair one for one belongs to no pool that can be named. A leg recomputed from the gross is what the hook requested, not what was paid, and should be labelled derived.

The Partner Executor

What The Hook's executor runs the arb recapture, and if it cannot, the trade goes through without it. This is the procedure for putting an executor behind a root.

  • Interface. IHookrArbExecutorV3 has routeAdmissionOpen(), executionBlockNumber() and executeArbitrage(ExecutionRequest). The adapter narrows the request to three arguments, executeArbitrage(PoolKey triggeringPool, address rebateRecipient, ProfitSplit split), and the executor must also answer feePolicyId().
  • One lawful waterfall. The registry reverts InvalidStackLimits() unless a pool's correctionFeePolicyId equals HookrWthFeePolicyV2.FEE_POLICY_ID, and every root shares one registry. A different waterfall needs a registry change, not a new root.
  • Order. The partner deploys the executor and Hookr deploys the adapter, the executor is configured to accept calls from it, the adapter is bound with setExecutorOnce, the owner registers it with registerIntegration, then seals a root profile naming it on a newly mined root. sealRootProfile succeeds once per kernel, so a new executor is a new root with its own kernel id and pools. There is no self-service path.
  • Admission checks the executor live. routeAdmissionOpen() must be true (InvalidIntegrationWiring() otherwise) and feePolicyId() must match the pool's frozen id (IntegrationMetadataMismatch otherwise).
  • Verify a binding from outside. wthExecutor() on the adapter, a second setExecutorOnce reverting ExecutorAlreadySet(), feePolicyId() against the pool's correctionFeePolicyId, stack(poolId) on the registry showing limits.correctionExecutor and a non-zero correctionExecutorIntegrationId, and GET /api/wth/pools on hookr.fun, which returns the root, its kernel id, the coordinator and the topic of each event.

Opening a Market on a Recapture Root

A market on the arb recapture root opens through the coordinator like any other market of its release. Two things differ: the kernelId names the recapture root, and the five StackLimits fields that are zero everywhere else are filled in.

FieldValueThe registry requires
correctionExecutor0x28AF…D806non-zero, with code, and the executor the root's profile admits
correctionCreatorthe address the creator share pays tonon-zero
correctionMaxVolumeBps5000above zero and at most MAX_CORRECTION_VOLUME_BPS, 5000
correctionMinProfitQuote1above zero
correctionFeePolicyId0xd2653e091cb7002585fd8b1192b58f11b9c951061dc797123e37d5e2ccb45cefequal to HookrWthFeePolicyV2.FEE_POLICY_ID
  • Gains. 40% of each arb recapture goes to correctionCreator, on top of everything the five Hook Blocks pay a creator, and the pool's LPs take 20% through Hookr's router and 40% through any other route.
  • Gives up. Every quote but native ETH; swaps arriving from inside a registered v3 pool's callback; the routing allowlist, which is per hook address, so nothing the default root carries reaches this root; and any swap sent with too little gas for the lane.
  • Getting it wrong is permanent. Leaving the five fields at zero opens a pool that can never recapture the arb, because the arb recapture path returns on its first line when correctionExecutor is zero. The fields are part of the stack hash and the root is named in the PoolKey. There is no repair.
  • Check the pair first. The lane does nothing unless the executor has registered a reference venue for the pair, and nothing enforces that at launch. A pair with no venue is admitted, opens normally and never recaptures the arb.
  • Admission rejects a zero executor with any other field set, a non-zero executor with a zero creator, volume or profit field, a non-native quote, and a wrong policy id (InvalidStackLimits()), an executor with no code or whose routeAdmissionOpen() is false or reverts (InvalidIntegrationWiring()), and one whose feePolicyId() differs (IntegrationMetadataMismatch).

Routing a Recapture Pool

On the arb recapture root the arb recapture is priced into the call and not gated by a floor, so a swap sent with a limit too small for it reverts instead of settling without one. An interface routing to these pools sizes from the ordinary estimate with headroom, and carries a measured floor on top for the case where the lane found nothing to do at estimate time. hookr-sdk 0.2.0 gives a pool on a superseded root no floor, so the caller sets one; 0.2.1 adds the correcting option.

Superseded Recapture Roots

RootWhat it does not do
Fourth, 0xE542…a8cCDoes not refuse a swap arriving from inside a registered v3 pool's callback. Recaptures in both phases.
Third, 0xa999…28CcDoes not recapture in beforeSwap: its kernel refused a quote-denominated swap, so every arb recapture it ran was in afterSwap.
First, 0xc7c5…68CCRecaptures the arb on neither phase in practice: an attempt needs more gas than an ordinary swap carries.

Every one carries the same 0x28cc flag word and shares the coordinator, registry and accounting kernel, and each has its own kernel id and adapter bound to the executor WTH ran at the time.

Launch Generations 3, 4 and 5

Every launch mints a fixed supply of 1,000,000,000 tokens with no owner, mint function, pause, blacklist or transfer tax, and the creator receives no token allocation. A pool's Hook Blocks are fixed when it opens, and the hook cannot be edited or replaced afterwards. Tokens launched under these generations remain readable and tradable under their own contracts, and the app reads them under the retained manifests. The contract addresses are listed under Contracts above.

Generations 3 and 4, Legacy

  • Bonding curve. 80% of supply sells across ten tranches, each priced 70% above the one before. Buys and sells work while the curve is live, and each pays a 1% curve fee split evenly between creator and protocol. The pool opens only after the last tranche sells out.
  • Graduation. The launch graduates in the same transaction as the last sale. The ETH raised and the remaining 20% of supply become a full-range Uniswap v4 position at exactly the price the curve ended on, with the creator's hook attached and a dynamic fee. The launcher owns it and has no function that removes liquidity. Supply that did not fit is burned by default, or placed as up to four token-only bands at creator-chosen price multiples above the graduation price.
  • Instant pool (generation 4). The creator commits ETH and chooses 20 to 100% of supply as the tradeable float, which together set the opening price and an opening valuation of 1 to 5 times the committed ETH. The pool opens with locked, launcher-owned liquidity in the launch transaction, with no curve phase. ETH accepted into the position cannot be withdrawn.
  • Fees. The locked position earns trading fees that anyone can collect. The ETH side splits by a share the creator chose at launch (50% by default, 80% at most), and the token side is burned. That share can be divided across up to four addresses, each credited on chain so a recipient can claim without depending on the creator.

Generation 5, V1

  • Instant pool. Zero-seed: the launch takes no ETH from the creator and the whole supply is one locked, launcher-owned sell position. The pool opens at a fixed platform-wide valuation of 2.5 ETH fully diluted, the same for every launch, and buyers supply all the ETH. LP Rewards is unavailable on this lane, because a zero-seed pool has no in-range LP at open to receive its donation.
  • Auction. The creator reserves a disclosed 20 to 50% of supply to seed the pool, and the rest is sold through Uniswap's Continuous Clearing Auction. Bidders set a budget and a maximum price, and each block clears at one price, and that price can rise as demand arrives. The floor valuation (default 0.22 ETH) and the graduation threshold, the minimum successful raise (default 1.1 ETH), are public before anyone bids.
  • Settlement. Ending at or above the threshold lets anyone trigger migration, and the pool opens at the final clearing price, with a dynamic fee, as a full-range position the launcher owns and cannot remove. At the 50% reserve the reserved tokens are worth the whole raise at the clearing price, so all of it locks and the creator is paid nothing; at a smaller reserve the un-locked balance is paid to the creator. Supply the position cannot absorb is burned. Ending below the threshold fails the launch: every bid is refunded, the supply is burned and no pool opens.
  • Fees. There is no curve fee. The launcher submits creatorFeeBps = 0 and feeRecipients = [], and zero activates the contract default: on an ETH pair, the collected quote-side position fees remaining after any guard withholding split 50% to the creator and 50% to the protocol, the creator side credits the launching wallet, and the token side is burned. This is launcher policy, not a restriction in the contract's ABI. On generations 3 and 4, token pages continue to show the fee routing stored for that launch. Every eligible ETH-pair swap also pays a flat 0.3% protocol fee. Anyone can collect it into the burner. Only its owner can spend it, on a buyback of $HOOKR through the canonical ETH/$HOOKR pool, capped per call, once per block, with a reviewed minimum output, burning every token bought in the same transaction.
  • $HOOKR pairs. Both lanes quote in ETH by default, and a launch can pair with $HOOKR instead: an instant open at a fixed 2,500,000 $HOOKR valuation, or an auction with a 600,000 $HOOKR floor and a 3,000,000 $HOOKR threshold. A $HOOKR pair supports Anti-Snipe and Hookr dynamic fees only, because Auto Burn, LP Rewards and the Nth-buy Pot account in native ETH. It pays no protocol fee, and 100% of the quote-side fees it collects go to the creator.

Hook Blocks on Generations 3 to 5

These are the earlier launcher's five blocks; which of them a launch could use depended on its lane and its quote, as above.

  • Anti-Snipe. For a set number of blocks after the pool opens, buys are capped and pay an extra LP fee. The blocks are counted on the parent chain, about 12 seconds each, not this chain's. Exact-output buys are blocked in the window so the cap cannot be routed around.
  • Hookr dynamic fees. The LP fee scales with how much of the pool's in-range depth a trade consumes, from the base fee up to the ceiling.
  • Auto Burn. A share of exact-input buy output goes to the dead address.
  • LP Rewards. An ETH-side share of an exact-input buy is donated to in-range LPs inside the swap.
  • Nth-buy Pot. An ETH-side share fills a pot and the Nth qualifying buy wins it. A public counter advances at most once per pool per block, a minimum buy applies, and the winner's payout can be redirected when claimed. The pot is not random.

Hook fee cuts apply to exact-input buys only. Sells and exact-output buys pay only the LP fee, plus the flat protocol fee on an ETH pair on generation 5, where an exact-output sell is exempt.

Market Figures and Limits

Hookr-indexed launch history uses settled trades. Pooled charts, volume and value deposited use live third-party market data, so read each figure with its source and window; an unavailable figure is not zero.

Nothing here carries a fairness, anti-bot or anti-MEV guarantee: the Anti-Snipe cap limits swap size for a window, identifies nobody and cannot stop someone using many wallets. The contracts were exercised end to end by a canary launch before writes opened, which is evidence and not an audit. Live-pool trades route only through the bounded Hookr router pinned in the manifest by address and runtime hash.

SDK 0.2

hookr-sdk 0.2 is the package for pools opened on the modular roots. It runs on viem and ships the addresses, the ABIs, the Hook Block catalog with its bounds, and builders for the calls an integration makes.

npm install hookr-sdk@0.2 viem
What it gives youExports
Addresses and chain facts for Robinhood Chain 4663, both roots and their kernel idsADDRESSES, ROOTS, KERNEL_ID, RECAPTURE_KERNEL_ID, CHAIN_ID, RPC_URL
The Hook Blocks a pool may compose, their parameters and bounds, and a validatorRULES, PARAMS, defaultBlock, validateStack
The fee model, so a UI can show the exact split before signingfees helpers
Market creation for both lanesbuildNewTokenMarketCall, buildExistingTokenMarketCall
Quotes through the trusted quoter and swaps through the trusted routerswap and quote builders
Reads of a pool's frozen configuration and market statereads helpers
Claims of pot payouts and creator feesclaims builders
Deep links into hookr.funlinks helpers

ADDRESSES.root is the default root and ADDRESSES.recaptureRoot the arb recapture root, and ROOTS pairs each with its kernel id. Pool keys, quotes, swaps and reads work on either root; pass the pool's own hooks to poolKeyFor. The market builders open on the default root unless root: ROOTS.recapture is passed; on the recapture root they freeze the five arb recapture fields from RECAPTURE_CORRECTION plus the creator, refuse any quote but native ETH and need the creator wallet. The swap builders put a 1,100,000 gas floor on a call against the arb recapture root; a pool on a superseded root gets none from 0.2.0, and 0.2.1 adds a correcting option with poolCorrects to read it from the pool's frozen blocks. On such a pool send the larger of the swap's own estimate plus a quarter and that floor, never less than the estimate. listRoots reads every kernel the registry has registered. listMarkets in 0.2.1 scans in 3,000,000-block windows, which a provider can cut short without an error, so scan logs on your own provider in windows of at most 5,000 blocks. RPC_URL is the public node and is for light reads only. A parity test checks the package against the app's encoders on each release.

ABIs and Errors

The ABI of each deployed contract is served as JSON from /api/v2-abi/<Contract>. Each file is the ABI from the release evidence, pinned to it by a test that compares the two byte for byte, and the route serves only the names in the table. HookrTokenV61 is the fixed-supply ERC-20 the token-deployer library creates for a new-token market; it has no address of its own, because one is deployed per launch. Every custom error in these ABIs is listed below by contract, generated from the same JSON, so it cannot fall behind the deployed code. An interface should surface these errors rather than retry: most are structural, and re-sending the transaction unchanged fails the same way.

ContractFunctionsEventsErrorsJSON
HookrModularHookV61837/api/v2-abi/HookrModularHookV6
HookrMarketCoordinatorV5381032/api/v2-abi/HookrMarketCoordinatorV5
HookrNativeMechanicsBlockV255615/api/v2-abi/HookrNativeMechanicsBlockV2
HookrStackRegistryV2651470/api/v2-abi/HookrStackRegistryV2
HookrKernelRouterV319128/api/v2-abi/HookrKernelRouterV3
HookrKernelQuoterV115018/api/v2-abi/HookrKernelQuoterV1
HookrTreasuryForwarderV114811/api/v2-abi/HookrTreasuryForwarderV1
HookrTokenV612022/api/v2-abi/HookrTokenV61

HookrModularHookV6

7 custom errors in its ABI.

  • AccountingKernelCodeChanged(bytes32, bytes32)
  • DelegateCallFailed()
  • InvalidHookData()
  • InvalidPayload()
  • InvalidStack()
  • InvalidWiring()
  • ReentrantCallback()

HookrMarketCoordinatorV5

32 custom errors in its ABI.

  • BadCallback()
  • CreatorTierUnchanged(address)
  • DefaultProtocolShareBpsUnchanged()
  • DuplicateMarket(bytes32)
  • ExcessiveSettlement()
  • InitialBuyAboveCap(uint256, uint256)
  • InitialBuyInputMismatch(uint256, uint256)
  • IntentAlreadyUsed(address, bytes32, address)
  • InvalidInitialBuy()
  • InvalidMarketArgs()
  • InvalidPayment(uint256, uint256)
  • InvalidStack()
  • InvalidToken()
  • InvalidWiring()
  • MarketOpeningPaused(address)
  • MarketOpeningPauseUnchanged()
  • NativeMechanicsModuleRequired()
  • NativeTransferFailed()
  • NoFoundingPosition(bytes32)
  • NotCreator(address, address)
  • NotOwner()
  • NotPendingOwner()
  • NotPoolManager()
  • ProtocolShareAboveCeiling(uint24)
  • ReentrantCall()
  • TaxedTransfer()
  • TierNotSet(address)
  • TokenSaltAlreadyUsed(bytes32, address)
  • TransferFailed()
  • UnexpectedToken(address, address)
  • ZeroAddress()
  • ZeroLiquidity()

HookrNativeMechanicsBlockV2

15 custom errors in its ABI.

  • ClaimTransferFailed()
  • ExactOutputBlockedDuringGuard()
  • ExternalLiquidityBlockedDuringGuard()
  • HookNotCalled()
  • InvalidConfig()
  • InvalidPotRecipient()
  • InvalidRuntimeContext()
  • MaxBuyExceeded(uint256, uint256)
  • NothingToClaim()
  • NotKernel()
  • NotPoolManager()
  • PartialFillUnsupportedWithInputCuts()
  • ProtocolShareNotEnforced()
  • StatefulKernelRequired()
  • ZeroAddress()

HookrStackRegistryV2

70 custom errors in its ABI.

  • ConfigTooLarge(bytes32)
  • CoordinatorAlreadySet()
  • CoordinatorNotSet()
  • DuplicateModule(bytes32)
  • DuplicateModuleImplementation(address)
  • ExceptionalKernelInstancesDisabled(bytes32)
  • ExclusiveGroupConflict(bytes32)
  • IntegrationAlreadyExists(bytes32)
  • IntegrationAlreadyRetired(bytes32)
  • IntegrationCodeChanged(address, bytes32, bytes32)
  • IntegrationFamilyMismatch(address, bytes32, bytes32)
  • IntegrationImplementationAlreadyRegistered(address, bytes32)
  • IntegrationKindMismatch(address, bytes32, bytes32)
  • IntegrationMetadataMismatch(address)
  • IntegrationNotActive(bytes32)
  • IntegrationOutsideRootProfile(bytes32, bytes32)
  • IntegrationPoolManagerMismatch(address)
  • IntegrationStackRegistryMismatch(address)
  • InvalidCurrencyPair()
  • InvalidHookFlagDependencies()
  • InvalidHookFlags(uint160, uint160)
  • InvalidIntegration()
  • InvalidIntegrationWiring()
  • InvalidKernel()
  • InvalidKernelInstance(address)
  • InvalidKernelInstanceFactory()
  • InvalidModuleConfig(bytes32)
  • InvalidModuleStackBinding(bytes32)
  • InvalidPoolKey()
  • InvalidRootProfile()
  • InvalidStackLimits()
  • KernelAlreadyExists(bytes32)
  • KernelAlreadyRetired(bytes32)
  • KernelCodeChanged(bytes32, bytes32)
  • KernelCoordinatorMismatch()
  • KernelInstanceFactoryAlreadyExists(address)
  • KernelInstanceFactoryAlreadyRetired(address)
  • KernelInstanceFactoryCodeChanged(address, bytes32, bytes32)
  • KernelInstanceFactoryNotActive(address)
  • KernelNotActive(bytes32)
  • KernelPoolManagerMismatch()
  • KernelStackRegistryMismatch()
  • MissingDependency(bytes32, bytes32)
  • ModuleCapsExceedStackLimits()
  • ModuleCodeChanged(bytes32, bytes32, bytes32)
  • ModuleConfigCapsExceedSnapshot(bytes32)
  • ModuleConflict(bytes32, bytes32)
  • ModuleGasExceedsStackLimit()
  • ModuleMetadataMismatch(bytes32)
  • ModuleOutsideRootProfile(bytes32, bytes32)
  • NonCanonicalProfileModuleOrder(bytes32, bytes32)
  • NotCoordinator()
  • NotOwner()
  • NotPendingOwner()
  • NotStackKernel(address, address)
  • RootImplementationAlreadyRegistered(address, bytes32)
  • RootProfileAlreadySealed(bytes32)
  • RootProfileIdentityAlreadyUsed(bytes32, uint32, bytes32)
  • RootProfileNotSealed(bytes32)
  • StableRootProfilesRequired()
  • StackAlreadyConfigured(bytes32)
  • StackAlreadyInitialized(bytes32)
  • TooManyModules()
  • UnknownIntegration(bytes32)
  • UnknownKernel(bytes32)
  • UnknownKernelInstanceFactory(address)
  • UnknownStack(bytes32)
  • UnregisteredIntegration(address)
  • UnsupportedHookFlags(bytes32, uint160, uint160)
  • ZeroAddress()

HookrKernelRouterV3

28 custom errors in its ABI.

  • BalanceQueryFailed()
  • CallbackNotActive()
  • CallbackNotCompleted()
  • CoordinatorCodeHashMismatch(bytes32, bytes32)
  • DeadlineExpired(uint256, uint256)
  • InputDebitMismatch(uint256, uint256)
  • InvalidAmount()
  • InvalidNativeValue(uint256, uint256)
  • InvalidRecipient()
  • InvalidSqrtPriceLimit()
  • InvalidStack()
  • InvalidWiring()
  • ModuleDataTooLarge()
  • NotCoordinator(address, address)
  • NotPoolManager()
  • OutputCustodyMismatch(uint256, uint256)
  • OutputDeliveryMismatch(uint256, uint256)
  • PartialFill(uint256, uint256)
  • Reentrancy()
  • RetainedBalance(address, uint256, uint256)
  • SettlementMismatch(uint256, uint256)
  • StackNotInitialized()
  • TooLittleReceived(uint256, uint256)
  • TooMuchRequested(uint256, uint256)
  • TransferFailed()
  • UnexpectedDelta()
  • UnexpectedNativeSender()
  • UntrustedStackRouter(address, address)

HookrKernelQuoterV1

18 custom errors in its ABI.

  • CallbackNotActive()
  • InvalidAmount()
  • InvalidAmountBound()
  • InvalidIdentity()
  • InvalidQuoteResult()
  • InvalidSqrtPriceLimit()
  • InvalidStack()
  • InvalidWiring()
  • ModuleDataTooLarge()
  • NotPoolManager()
  • QuoteResult(bytes32, uint128, uint128)
  • Reentrancy()
  • SimulationDidNotRevert()
  • StackNotInitialized()
  • TooLittleReceived(uint256, uint256)
  • TooMuchRequested(uint256, uint256)
  • UnexpectedDelta()
  • UntrustedStackQuoter(address, address)

HookrTreasuryForwarderV1

11 custom errors in its ABI.

  • InvalidNativeBlock()
  • InvalidPoolManager()
  • InvalidTarget()
  • NativeBlockUnchanged()
  • NativeTransferFailed()
  • NotBound()
  • NotOwner()
  • NotPendingOwner()
  • TargetUnchanged()
  • TokenCallFailed()
  • ZeroAddress()

HookrTokenV61

2 custom errors in its ABI.

  • MathOverflowedMulDiv()
  • ZeroAddress()