The correction trust boundary
Read what a pool on a recapture root trusts, what a reviewer can check from outside, what is unpublished, and which way the failure falls.
A pool on a recapture root calls a contract Hookr did not write, inside the swap, twice. This page states what that means: what a reviewer can check from outside, what is not published, which way the failure falls, and what bounds the damage. Arbitrage recapture describes the mechanism itself.
What Runs Inside the Swap
A pool on 0xb914f955294799de4b891bd2EA8AF628Fa1c68CC runs the same five native rules as a pool on 0xb3cA29cF721380CEe8b8e4755F3865Ebc68Fe8cC. The recapture root shares the default root's coordinator, its native mechanics block and its stack registry at 0x5b7f1A117A83aaac15B0698Aa4eB9D53fE5f5BA3; the root hook address and the kernel id are what differ. What it adds is a lane outside the five rules: after the modules have priced the swap, the kernel hands the pool and the swap to a correction executor, once in beforeSwap and once in afterSwap.
Hookr's own contract in that lane is an adapter, 0x28AF7A3645080e926a3101461e0Ec0594D42D806. It holds no funds, decides no amounts, and forwards the call to one executor. The executor is 0xc356cf51134e0DF02BFE880115DD8c66Ead45803, written and operated by WTH. It decides what to trade, how much, against which venue, and at what price, and it settles its own trade against the PoolManager.
What Is Verifiable From Outside
| Claim | The read |
|---|---|
| This pool's root, and that the pool cannot change it | stack(poolId) on 0x5b7f1A117A83aaac15B0698Aa4eB9D53fE5f5BA3: kernel, and the stack hash it is checked against on every callback |
| This pool's five frozen correction fields | the same read: limits.correctionExecutor, correctionCreator, correctionMaxVolumeBps, correctionMinProfitQuote, correctionFeePolicyId |
| The registry admitted a correction integration for it | correctionExecutorIntegrationId on the same struct, non-zero |
| The adapter names this executor | wthExecutor() on the adapter, selector 0x477a9d7c |
| The binding cannot be moved | a second setExecutorOnce reverts ExecutorAlreadySet(), selector 0xa1002dd1, called from the owner 0xF4Ab4698554D5c95874986d5e956c62e5E6aB3eE; from any other address onlyOwner answers first with NotOwner(), 0x30cd7471 |
| The waterfall the pool committed to | feePolicyId(), selector 0xc893dd97, against the pool's frozen correctionFeePolicyId |
| Every correction the pool has ever run | CorrectionAttemptSucceeded, CorrectionAttemptFailed and CorrectionAttemptSkipped on the root, indexed by pool id and phase |
The root's profile read sealed on 2026-09-12, and sealRootProfile succeeds once per kernel. So the executor a root points at is fixed for the life of that root, and a different executor requires a newly mined root hook with its own kernel id, its own profile, and its own pools.
Where the Published Set Stops
The executor's source is not verified on the explorer. What it does with a correction is visible only through its effects: the profit it reports, the payouts it makes, and the views it exposes. Nothing on Hookr's side constrains its behaviour beyond the call it is handed and the fact that it must settle before the transaction ends.
The current root's own contracts are published; three earlier roots' are not. The public contracts repository now exports HookrModularHookV6WthV5, HookrSwapKernelV5Wth, HookrModularCorrectionLibV3, HookrWthExecutorAdapterV1 and the two interfaces they need, each with its own source commit and sha256 in the source manifest, alongside the default-root release. The three superseded recapture roots are not exported, so a reviewer looking at a pool on one of those has the deployed bytecode and nothing more.
The pin is not yet checkable by a third party. The manifest's self-verification by sha256 passes, but the commit the six recapture files are pinned at is not reachable on any public remote of the private source repository, so the manifest's --source-git check cannot be run against it from outside until that commit is pushed. Read the export as byte-checkable against itself today and as provenance-checkable later.
The fallback split is now readable. A swap that arrives without an authenticated trader pays 40% to the triggering pool's LPs instead of 20%, which is the ordinary case for Universal Router and aggregator flow. HookrModularCorrectionLibV3.sol:46-48 hard-codes it as FALLBACK_TRADER_BPS 0, FALLBACK_CREATOR_BPS 4,000 and FALLBACK_TRIGGER_POOL_BPS 4,000; :82-87 writes those into every request and :88-92 replaces them with the router-route shares only when the authenticated recipient is non-zero. What is not readable is the payment: the executor allocates and pays every recipient itself, so the split the hook names is what was requested, not what was observed.
Fail-Open Is the One Deliberate Exception
Everything else in Hookr fails closed: a read that reverts, a code hash that moved, a token that refuses a transfer, all of them revert the transaction rather than settling on a guess. See Threat model.
The correction is the exception, on purpose. The kernel wraps the partner call in a try/catch, so a revert on the executor's side becomes a skipped correction and an event, never a failed swap. A trader on a correcting pool cannot have their swap broken by the executor failing, and the price of that is that a swap can settle having captured nothing while looking exactly like one that captured nothing because there was nothing to capture. CorrectionAttemptFailed and CorrectionAttemptSkipped are the only difference a reader sees, and they carry a reasonHash rather than a reason.
Fail-open covers a revert inside the correction. It does not cover gas. HookrModularCorrectionLibV3 removed the admission check the V2 library made on gasleft(), so a swap sent with a fixed limit too small to cover the correction can run out of gas inside the executor call and revert the whole swap, and the library's note at :30-36 says that is deliberate. SKIP_LOW_GAS is declared at :55 and never emitted on this root.
So eth_estimateGas prices the correction into its own answer on this lane, and the failure mode for a caller who overrides that answer downwards is a reverted swap rather than a skipped correction. Routing a correcting pool has the sizing.
Hookr Enforces No Profit Floor and No Volume Cap
A correcting pool freezes correctionMaxVolumeBps and correctionMinProfitQuote, and neither is a bound Hookr applies.
They are present because the registry requires them: admission rejects a zero profit floor and a volume field outside 0 < maxVolumeBps <= MAX_CORRECTION_VOLUME_BPS. Nothing downstream reads them as a cap, because the executor's interface takes neither number. The live pools therefore carry the registry's permissive maximum, 5000 bps, and its smallest legal floor of 1 wei, rather than numbers that would read as a limit Hookr applies and does not. Sizing and the profit floor are the executor's own, and a reviewer should read them as a declaration of the fields' presence and nothing more.
The Refusal in beforeSwap
The current recapture root refuses one class of swap outright. Before correcting, beforeSwap staticcalls the executor's MEV view and reverts when it returns a clean true, meaning a registered v3 pool for the pair is locked for re-entrancy and the caller is inside that pool's callback. It accepts both spellings the executor may carry, checkV3PoolsMev at 0x6075521e and checkV3PoolsMEV at 0x85c30352.
Two things follow. Ordinary MEV on a correcting pool does not behave as it does on a plain v4 pool: an arbitrage bot that moves the reference venue first and closes its leg here is refused rather than served. And the refusal is a trust decision made on the partner's answer, since the view is the executor's. It is placed in the hook rather than the adapter precisely because the adapter call sits inside the try/catch, where a refusal would be swallowed into a skipped correction.
What Bounds the Damage
- The adapter holds no funds and no balances. Nothing accrues in it, so there is nothing in it to take.
- The binding is one-shot and already spent, so the adapter cannot be repointed at another contract by anyone, including the Hookr owner.
- The root profile is sealed, so the pool's executor cannot be changed after the pool opens, and the five correction fields are inside the stack hash.
- No owner function reaches an open pool, on this root as on the default root. See Immutability and ownership.
- The lane pays out of a correction's realised profit. A trader's swap carries no additional cut for it, and the base LP fee is untouched.
Two ways the lane can still break a swap, which the list above does not bound: the beforeSwap refusal, and gas. A swap on this root can revert with MevCallbackRefused on the partner's answer, and a swap sent with a limit too small for the correction can run out of gas inside it. Neither is a fail-open outcome and neither emits a Skipped event.
The split itself is settled by the executor and is observable on chain. A correction on the current root realised 0.016194530171421913 ETH in a single transaction and paid it out in the frozen proportions: 40% to the creator, 20% to the trader, 20% donated to the pool's liquidity providers, 10% to WTH and 10% to Hookr. Every one of those figures reproduces from the constants this page cites, which is what a reviewer can check without the executor's source.
Where the Lane Stands
The contracts are deployed and sealed on chain 4663. The option is not offered to creators in the Hookr app: the picker card renders and its button is disabled, and the launch path reads the registry's sealed profile before any launch regardless of that flag. Uniswap's own routing allowlist covers the default root and not this one, and what Uniswap decides about it is not something this page can state. Hooklist and routing is where that stands.