Where the numbers come from
Read which figures on hookr.fun are read from chain, how often each surface refreshes, and what an unavailable or partial figure means.
On this page
Every figure hookr.fun prints is produced one of three ways: read from a contract at a named block, measured from settled events, or taken from a third-party market provider. This page is the one place that says which is which, so the product pages can show the figure instead of explaining it.
One principle runs underneath all of it. A value that could not be read is never shown as zero: it renders as an em dash with its reason, and any control that depends on it stays disabled until a real read replaces it.
Balances and the Pooled column
A pool's two token amounts are read from the Uniswap v4 PoolManager singleton: the pool's own balances at one pinned block height, scaled by each token's decimals. Rows that rank by Pooled rank by the value of those balances. That is the pool's reserve, not token market cap and not a dollar oracle value.
Those reads do not happen inside the request that renders the table. A cohort of a hundred-odd pools costs a pinned tick-ledger read each, far more than a page render can pay for, so a scheduled sweep performs them and writes each answer to a warm ledger that a request simply reads back.
Each warm record is stamped with the block it was read at and carries that block to the row. A balance read ten minutes ago is truthful about the block it names and would be a lie about the current head, so the block travels with the figure rather than being re-dated to the head, and a pool whose read did not complete stores the reason instead of an amount.
How often each surface refreshes
| Surface | Cadence |
|---|---|
Pool balance sweep, / | every 5 minutes |
Market index sweep, / | every 5 minutes |
| Pool and token tables in an open tab | every 60 seconds while the tab is visible |
| Token and pool detail pages in an open tab | every 20 seconds while the tab is visible |
A tick is skipped while the tab is hidden and runs again when it becomes visible, so a backgrounded page holds its last verified read instead of burning refreshes nobody is watching.
Total APR
Every pool APR on hookr.fun, on the pool page, a position card, the landing, Discover and /launchpads, is one figure: Total APR. It is what a pool paid its LPs since it opened, annualised over its age and the value of every position in the pool now. Its parts come from the pool-event index, which records each pool's swaps, liquidity changes and donations.
| Part | What it counts |
|---|---|
| Swap fees | The LP fee each swap actually paid, as the swap itself records it, which a dynamic-fee hook can set per swap |
| LP rewards | The root hook's donations to LPs |
| Arb recapture | The LPs' share of each price arb recapture, donated into the pool in the arb recapture's own transaction |
| Other donations | Any other donation to the pool, when it has one |
LP rewards and arb recapture reach LPs as Uniswap v4 donations into fee growth, so a position's Fees earned, the figure Uniswap shows, already includes them. Hookr splits Fees earned into these parts rather than adding recapture on top, and Uniswap's own APR counts swap fees alone.
The figure is the pool's lifetime rate, marked "Since open": everything it paid its LPs since it opened, annualised over the time it has been open, with no shorter window beside it. The denominator is the value deposited in the pool now: every position's two tokens at the pool's price at the index's head block, in the pool's quote currency. Each figure is stamped with that block, and with how far behind the chain the index is once it passes five minutes.
A figure that cannot be read is unavailable with its reason, never 0%:
- a pool under a day old, or one with no trades since it opened
- a pool that charges a protocol fee, since the index cannot split the LP's part of each swap fee out of it
- a pool the event index does not cover, or an index that refused or did not answer the read
- a pool with no liquidity to measure against
A pool holding less than $1,000 at that price says "Too little liquidity to rate" in place of every figure. A yearly rate tells you what a deposit would earn, and below $1,000 one ordinary deposit would more than double the pool and cut its rate in proportion. The pool's value is priced in US dollars with the app's own token price, ETH from Chainlink's ETH / USD feed, and a pool that could not be priced says so instead.
The floor does not correct for liquidity that left since the pool opened. Every figure divides the pool's income since it opened by what it holds now, so income earned before most of the liquidity left is divided by what stayed. Under $1,000 the floor hides that figure. Above it, such a pool can read higher than its LPs earned.
When only some parts read, those parts show under their own labels and no total is printed.
A position card also shows its Lifetime APR: that position's lifetime fees over its time-weighted principal. The agent API's GET /api/pool-apr still serves the older estimate from 24-hour swap volume and the pool's LP fee rate, which leaves out arb recapture.
Why some pools show no market data
Price and provider value deposited come from GeckoTerminal, not from Hookr's own index, and so does 24-hour volume for a pool that index does not cover. A pool that provider has not listed shows no market figures at all. That is an absence of provider coverage, not a claim that the pool is empty or inactive. Its onchain balances are read separately and still appear.
Depth is only comparable between pools quoted in the same currency. A pool quoted in native ETH and a pool quoted in an ERC-20 are two different units, and no table adds them together.
All-time totals
/api/protocol-stats totals pool swaps and bonding-curve trades on the hooks listed in lifetime.hooks, through the block lifetime.throughBlock, one figure per quote asset in volumeByQuote. Rehearsal markets, hidden tokens and Hookr's experimental canary hooks are left out. volumeWei is the ETH figure alone, and traders is not counted.
If one pool in an asset is not indexed yet, that asset's total is unavailable and names the pool, never a smaller sum. The trade count then waits for every asset, so a newly opened pool can leave both unavailable until the index covers it.
The landing's Value facilitated is a different figure: lifetime swaps through Hookr hooks from the pool-event index, one side valued in USD at current prices.
What "partial" coverage means
The pool and launch listings are assembled from several registries plus the pool index. When one of those does not answer, the snapshot is marked partial, and that means exactly one thing: the list is incomplete, not short.
Counts derived from a partial read are floors, and a floor is marked with ≥ rather than printed as if it were exact. A partial read never drops a row that was proven to exist and never invents one that was not. Where a figure cannot honestly be stated even as a floor, it is withheld.
Wallet actions never trust any of this
Warm balances, indexed history and provider figures exist for navigation and aggregates. Every action that can move value re-reads the relevant onchain state immediately before preparing the wallet request, and a read that fails leaves the action disabled rather than letting a cached row stand in for it.
Data and safety methodology carries the same standards for profiles, social provenance and earnings language.