Open the app

HookrModularHookV6

Read about the root hook every single Hookr pool names: its exact permission bits, and precisely what it always delegates to.

Deployed at 0xb3cA29cF721380CEe8b8e4755F3865Ebc68Fe8cC, compiled from src/HookrModularHookV6.sol at source commit 8db7fc940938f811f508ba9cb0c8f2d3f24c9a25. Its source is verified on the explorer; see Verification. ABI: /api/v2-abi/HookrModularHookV6.

Inherits: HookrSwapKernelV3

The root hook. One address serving every Hookr pool, with per-PoolId frozen state

The contract body is a constructor that forwards to HookrSwapKernelV3 and the two identity getters. Everything else on this page is inherited from HookrSwapKernelV3, which handles beforeSwap and afterSwap itself and forwards every other selector to HookrSwapAccountingKernelV3 by DELEGATECALL. The separate name gives the deployed root its own identity and its own CREATE2 mining target.

Identity

Two pure getters every contract in the graph carries, so an integrator can confirm what it is holding without a bytecode comparison.

function contractName() external pure returns (string memory);   // "HookrModularHookV6"
function contractVersion() external pure returns (string memory); // "6.0.0"

Constants

NameTypeValue
REQUIRED_FLAGSuint1600x28cc (10444)
KERNEL_FAMILY_IDbytes32keccak256("HOOKR_SWAP_DELTA_V1")
KERNEL_INSTANCE_LAYOUT_IDbytes32keccak256("HOOKR_KERNEL_INSTANCE_LAYOUT_V1")
STATEFUL_MODULE_MAGICbytes32keccak256("HOOKR_STATEFUL_SWAP_MODULE_V1")
DYNAMIC_FEE_FLAGuint240x800000
MIN_SQRT_PRICE_LIMITuint1604295128740
MAX_SQRT_PRICE_LIMITuint1601461446703485210103287273052203988822378723970341

State Variables

NameTypeDescription
poolManagerIPoolManagerImmutable. The v4 singleton
stackRegistryIHookrStackRegistryV1Immutable. Source of every pool's frozen stack
coordinatoraddressImmutable. The only address allowed to initialize a pool
accountingKernelHookrSwapAccountingKernelV3Immutable. The DELEGATECALL target
accountingKernelCodeHashbytes32Immutable. Checked before every delegation

Functions

constructor

Cross-checks every wiring fact before pinning it: the accounting kernel must report the same PoolManager, stack registry, coordinator, kernel family, layout id, required flags and stateful magic, and every dependency must have code. Reverts InvalidWiring otherwise.

constructor(
    IPoolManager poolManager_,
    IHookrStackRegistryV1 stackRegistry_,
    address coordinator_,
    HookrSwapAccountingKernelV3 accountingKernel_
);

Parameters

NameTypeDescription
poolManager_IPoolManagerThe Uniswap v4 singleton
stackRegistry_IHookrStackRegistryV1Registry holding each pool's frozen stack
coordinator_addressThe market coordinator
accountingKernel_HookrSwapAccountingKernelV3Implementation the hook delegates its callbacks to

beforeSwap

Resolves the pool's frozen stack and requires the PoolKey to match it, authenticates the caller against the pool's trusted router and quoter at their registered code hashes, decodes hookData only for a trusted caller, then delegates to the accounting kernel with a re-encoded envelope that carries the payer, the recipient, the stackHash and the module payload, with the correction legs stripped; for an untrusted caller the raw hookData is forwarded unchanged. A call from anything other than the PoolManager is forwarded to the accounting kernel, which rejects it.

function beforeSwap(address sender, PoolKey calldata key, SwapParams calldata params, bytes calldata hookData)
    external
    returns (bytes4 selector, BeforeSwapDelta delta, uint24 feeOverride);

Parameters

NameTypeDescription
senderaddressThe address that called swap on the PoolManager
keyPoolKeyThe pool being swapped
paramsSwapParamsDirection, signed amount and price limit
hookDatabytesEmpty from an untrusted caller; an authenticated envelope from the trusted router or quoter

Returns

NameTypeDescription
selectorbytes4IHooks.beforeSwap.selector
deltaBeforeSwapDeltaQuote taken off the specified currency
feeOverrideuint24Effective LP fee for this swap, with the override flag set

Reverts InvalidStack when the stack is missing, uninitialized, bound to a different kernel or code hash, or when the key's currencies, fee or hooks disagree with it, and also when the trusted router or quoter's runtime code hash has changed. Reverts InvalidHookData when a trusted caller's envelope is too short or is not bound to the pool's stackHash.

afterSwap

Same resolution and authentication, then delegates. Returns the unspecified-currency delta carrying the protocol share and the burn.

function afterSwap(
    address sender,
    PoolKey calldata key,
    SwapParams calldata params,
    BalanceDelta delta,
    bytes calldata hookData
) external returns (bytes4 selector, int128 hookDelta);

Returns

NameTypeDescription
selectorbytes4IHooks.afterSwap.selector
hookDeltaint128Amount taken on the unspecified currency

Every other selector

The contract's fallback re-checks the accounting kernel's code hash and forwards the raw calldata to it by DELEGATECALL, returning whatever it returns. beforeInitialize, beforeAddLiquidity, syncBaseFee, inFlight and previewQuoteTax are therefore served at the root's address, and the kernel's own reverts surface unchanged, including HookNotCalled for the callbacks this hook does not implement. Reverts AccountingKernelCodeChanged(expected, actual) if the kernel's code hash has moved. A kernel revert bubbles up with its own data; DelegateCallFailed is raised only on the beforeSwap and afterSwap path, when the kernel reverts without any data.

contractName

function contractName() external pure returns (string memory);

Returns "HookrModularHookV6".

contractVersion

function contractVersion() external pure returns (string memory);

Returns "6.0.0".

statefulModuleKernelMagic

The opt-in marker a registry requires before it will freeze a STATEFUL_V1 module against this root.

function statefulModuleKernelMagic() external pure returns (bytes32);

kernelInstanceLayoutId

The storage-layout lineage identifier.

function kernelInstanceLayoutId() external pure returns (bytes32);

The Correction Lane

HookrSwapKernelV3 carries a correction lane for pools whose frozen StackLimits name a correction executor. After the accounting kernel returns, a trusted caller's correction payload can trigger one bounded, fail-open correction attempt per phase, reported by the three events below and guarded against nested callbacks by ReentrantCallback.

The root profile this release seals names no correction-executor integration, and createStack rejects any stack whose correctionExecutor is not the profile's, so every pool on this root freezes all-zero correction fields and the lane never runs. The stack check still requires those fields to be consistently zero on every callback and reverts InvalidStack otherwise.

Events

CorrectionAttemptSucceeded

event CorrectionAttemptSucceeded(
    PoolId indexed poolId, uint8 indexed phase, bytes32 indexed planDigest, uint256 realizedProfitQuote
);

CorrectionAttemptFailed

event CorrectionAttemptFailed(PoolId indexed poolId, uint8 indexed phase, bytes32 payloadHash, bytes32 reasonHash);

CorrectionAttemptSkipped

event CorrectionAttemptSkipped(PoolId indexed poolId, uint8 indexed phase, bytes32 payloadHash, bytes32 reasonHash);

Errors

ErrorWhen
InvalidWiring()a constructor argument has no code or the accounting kernel reports different wiring
AccountingKernelCodeChanged(bytes32 expected, bytes32 actual)the accounting kernel's runtime code hash differs from the one pinned at construction
InvalidStack()the pool has no initialized stack for this root, the PoolKey disagrees with it, or a trusted integration's code hash has changed
ReentrantCallback()a nested callback arrives outside the correction lane's expected state
DelegateCallFailed()a delegation fails with no revert data
InvalidHookData()a trusted caller's hookData is shorter than an envelope, carries the wrong version, a zero payer or recipient, is not bound to the pool's stackHash, or is not the canonical encoding
InvalidPayload()a trusted caller's correction payload is malformed

Address Mining

The address must satisfy address & 0x3fff == 0x28cc, which is why it is mined: a CREATE2 salt through HookrReleaseCreate2FactoryV1 produces an address with those low bits. See Hook permissions.