Open the app

Events for indexers

Read which deployed Hookr contract emits what event, and how an indexer keeps the six value streams apart from each other.

Which contract emits what, and how to keep the streams apart. Every event below is on a deployed Hookr contract; the PoolManager's own Swap, Initialize and ModifyLiquidity are unchanged and still the source of truth for price and depth.

Stream Separation

Six value streams run through a Hookr pool and they must not be summed together. Each has exactly one authoritative source.

StreamDenominated inSource eventNotes
Base LP feeboth currenciesPoolManager fee growthNot a Hookr event. Read it from the position or from the pool state
Surge and snipe surcharge, LP partboth currenciesPoolManager fee growthArrives as ordinary fee growth through the fee override; indistinguishable from the base fee on chain
Protocol sharequoteProtocolShareAccruedOne event per stream per swap
LP-reward donationquoteLpRewardsDonatedDonated in-swap, so it lands as fee growth for whoever is in range
PotquoteHookFeesAccrued for inflow, JackpotHit for payoutThe pot balance is also readable as potWei(poolId)
BurnsubjectAutoBurnThe protocol's slice of the burn appears in ProtocolShareAccrued, in quote

The LP part of a surcharge cannot be separated from the base fee by watching events, because both arrive as the same v4 fee growth. To split them, recompute: the pool's baseFeePips is in its frozen StackLimits, and the effective fee for a swap is recoverable from the swap's own amounts. Do not present a guessed split as measured.

Per-Pool Counters

Cheaper than replaying events, and always consistent with them.

potWei(poolId)                        // current pot balance
potBuyCount(poolId)                   // qualifying buys counted
guardLpEarnedQuote(poolId)            // cumulative guard-window quote LP earnings
totalHookFeesWei(poolId)              // cumulative LP-reward plus pot cuts
totalBurnedTokens(poolId)             // cumulative subject burned
totalLpDonatedWei(poolId)             // cumulative LP donations
totalPotPaidWei(poolId)               // cumulative pot payouts
totalProtocolShareWei(poolId)         // cumulative protocol share, all streams
protocolShareByStream(poolId, stream) // cumulative protocol share for one stream

guardLpEarnedQuote is what separates guard-window earnings from post-guard earnings for a founding position. Both are paid to the same lpFeeRecipient, so the counter is the only way to show them as two lines.

HookrMarketCoordinatorV5

EventWhenIndex on
MarketCreateda market is openedpoolId, subject, creator
ProtocolShareResolvedsame transaction as MarketCreatedpoolId, creator
CreatorBuyExecutedan initial creator buy settlespoolId, subject, creator
LpFeesCollectedcollectLpFees runspoolId, recipient
CreatorTierSet, CreatorTierClearedowner changes a tiercreator
DefaultProtocolShareBpsSetowner changes the default share for future poolsnothing indexed; one uint24 argument
MarketOpeningPauseSetowner pauses or unpauses openingnone
OwnerProposed, OwnerSetownership transferpendingOwner on the proposal, owner on the set

MarketCreated carries the origin, so filter on it to tell the two lanes apart. origin == NEW_TOKEN has a founding position and possibly a guard window; origin == EXISTING_TOKEN has neither, and its creator records only who opened the pool.

HookrNativeMechanicsBlockV2

EventWhenIndex on
HookFeesAccruedan exact-input buy takes an LP-reward or pot cutpoolId
ProtocolShareAccruedany stream credits the protocol sharepoolId, stream
LpRewardsDonatedquote is donated to in-range LPspoolId
JackpotHitthe pot pays outpoolId, winner
AutoBurnsubject output is burnedpoolId
Claimedan account pulls its claim balancequote, account, to

The stream on ProtocolShareAccrued is a typed, indexed enum, so an indexer can filter on it directly:

enum ProtocolStream { Surcharge, Guard, LpReward, Pot, Burn }

Guard only ever accrues on an exact-input buy inside a guard window, so guard-window protocol revenue is separable without arithmetic. A deferred slice taken in afterSwap is always pure surge and reports as Surcharge.

HookFeesAccrued.burnWei is always zero. The burn is reported by AutoBurn, in subject units.

Claimed covers three different things: a pot win, a royalty payment, and the treasury forwarder's own collection. Distinguish them by the account: the forwarder's address for the protocol share, royaltyTo for royalties, a trader for a pot win.

HookrSwapAccountingKernelV3

Emitted from the root hook's address, because the kernel runs by DELEGATECALL. Point your indexer at the root hook, not at the kernel.

EventWhen
MarketInitializedthe coordinator initializes a pool
ModuleFeeAccrueda module's take is credited
ModuleFeeSkippeda take was requested but could not be credited
StatefulModuleActionper stateful module callback
HookFeetotals for one swap, in both currencies

ModuleFeeSkipped is worth alerting on. It means a recipient could not receive a credit.

HookrKernelRouterV3

EventWhenIndex on
SwapExecuteda swap settles and output is deliveredpoolId, stackHash, payer

Only swaps routed through the Hookr router emit this. A Universal Router swap on the same pool emits nothing here; use the PoolManager's own Swap for a complete picture, and treat SwapExecuted as the subset that carried an authenticated payer and recipient.

HookrTreasuryForwarderV1

EventWhenIndex on
Collectedan accrual was pulled and deliveredquote, target
ForwardDeferredan accrual was pulled but the target refused itquote, target
Swepta held balance was pushed to the targettoken, target
TargetSet, NativeBlockSetowner rewires the forwardertarget, nativeBlock
OwnerProposed, OwnerProposalCleared, OwnerSetownership transferpendingOwner on the proposal, owner on the set; OwnerProposalCleared has no arguments

Protocol revenue actually received is Collected plus Swept, not ProtocolShareAccrued. Accrual is what the pool owes; collection is what reached the target. A dashboard should show both, labelled as accrued and collected.

HookrStackRegistryV2 and HookrModuleCatalogV1

Two registry events mark a pool's birth and are worth indexing: StackConfigured(poolId, stackHash, kernelId, subject, quote, moduleCount) when the coordinator freezes the stack, and StackInitialized(poolId, stackHash, kernel) when the kernel marks it initialized. Everything else on the registry and the catalog is administrative: ModuleRegistered, ModuleRetired, CanonicalStatefulModuleSet, CoordinatorSet, IntegrationRegistered, IntegrationRetired, KernelRegistered, KernelRetired, KernelInstanceRegistered, KernelInstanceFactoryRegistered, KernelInstanceFactoryRetired, ExceptionalKernelInstanceRegistered, RootProfileSealed, plus ownership events. None of those reaches a pool that is already open.

Reconstructing a Pool's Configuration

Do not parse it out of events. Read it:

HookrModuleTypesV1.StackCore memory core = registry.stack(poolId);
(HookrModuleTypesV1.ModuleSnapshot memory m, bytes memory config) = registry.moduleAt(poolId, 0);
HookrNativeMechanicsBlockV2.Config memory cfg = abi.decode(config, (HookrNativeMechanicsBlockV2.Config));

The config is frozen, so one read at any block is correct for the life of the pool.