# Undertow receipt: `undertow-receipt/1`

A receipt is one plain JSON document per X-ray view: a standard self-serve economic assessment connecting **funding/capture, actual execution, beneficiaries/assets/rights, and controllable risks**, with the evidence blocks and coverage needed to check it. It is not a monitoring job, safety score, income guarantee or forecast.
It is built by `xrayReceipt(X)` (`app/hooks_template.html`, section "receipt (JSON)") from `window.__XRAY__ = {M, fam, at}`.
Both exist only in the scan build (`dist/gen.html`, `dist/full.html`): the public page (`dist/index.html`, `dist/claus/`) is built without them
and without receipt files (owner decision, 22 Oct). It presents the same economic read for humans and points agents to the paid lookup
in its Evidence card ("JSON for agents: 1 USDG over x402").

- **With the snapshots:** `node app/gen_snapshots.mjs` writes `app/receipts/<symbol>[-eth].json` from the same page evaluation as the
  snapshot (`--receipts dir` to write elsewhere). `node app/lookup/build.mjs` bundles them for the paid lookup (cached receipts, `max_age`).
- **Paid lookup (x402 v2, 1 USDG):** `GET /undertow?token=0x…[&chain=rh|eth]` on bucks-check (no chain: detected as on the page, Robinhood
  Chain first, then Ethereum, where the address has code) runs the same page's live scan in Node (`app/lookup/engine.mjs`: the built page
  under jsdom; `node app/lookup/build.mjs` bundles it) and returns its receipt plus `lookup`.

The schema id is Bucks' original (`deploy/undertow-claus/receipts/make_receipt.py`, Pons V2 only): every field that file writes keeps its
name and meaning here; everything else is added. Readers should ignore fields they do not know.

## Conventions
- Addresses and ids are lowercase `0x…` strings. Blocks are numbers (chain's own block numbers; Robinhood Chain = the L2's).
- **Amounts** are `{raw, human}`: `raw` = decimal string in the token's smallest unit (exact), `human` = `raw / 10^decimals` as a number
  (12 significant digits, for reading only). Volumes are token-side (the scanned token's units).
- **Percent fields** end in `Pct` and are percent numbers (1.8 = 1.8%). `share`, `lpFee` and texts are strings exactly as the page shows them.
- `null` = not read, not applicable or not established; it never means zero.
- **Text** fields (`who`, `observed`, switch `text`) are the page's own sentences with markup removed; every address or tx hash the page
  links appears in full.
- Fees forgone and USD values are **estimates**, as on the page (see `research/FEE_CAPTURE.md`).

## Fields

| Field | Type | Meaning |
|---|---|---|
| `schema` | `"undertow-receipt/1"` | |
| `by`, `note` | string | Attribution and the page's disclaimer (Bucks' wording), plus: `split[].paidOnTo` is what the page shows as "Paid on"; where `onwardSplitDecoded` is false the page does not show who ends up with that share. |
| `generator` | object | `app` = "Undertow"; `source` = `"live scan"` or `"snapshot"` (a preloaded gallery scan); `scannedAt` = ISO time the scan finished; `view` = `"token"` or `"pool"` (`#0xTOKEN/0xPOOLID`, the diagram shows that pool). |
| `chain` | `{id, name}` | 4663 Robinhood Chain, 1 Ethereum. |
| `block` | number | The block state was read at (first of `blocks.stateReadAt`; the scan's head when none was recorded). Same as Bucks' `block`. |
| `blocks` | object | `head` = the scan's head block (log scans end here); `stateReadAt` = every block a state read was answered at (normally one: the head; more if the node had dropped it and the read re-pinned); `stateReadAtLatest` = true if reads fell back to the node's latest block; `window` = `{fromBlock, toBlock}` of the ~24 h window (inclusive). |
| `token` | object | `address`, `symbol`, `decimals`, `totalSupply` (amount). |
| `hook` | object | `family` (the page's name for the hook, e.g. "Pons V2"; null for a plain pool), `address`, `callbacks` (permission flags from the address bits), `adapter` (the page's reader: `pons`, `claus`, `hookr`, … `generic`, `plain`), `sourceVerified`. Abyss pools: `family` "Abyss (exchange, v3-style, not a v4 hook)", `address` null, `pool`. Another DEX shown (no official pool, its busiest venue is not on Uniswap v4): `family` null, `address` null, `callbacks` [], `adapter` `venue`, `pool` (the pool contract). |
| `officialPool` | object or null | The token's official pool: `id`, `createdAtBlock`, `pairedWith` (symbol), `pairedWithAddress` (`0x000…0` = ETH), `hook`, `family`, `rule` (`"urc5"` = declared by the token's URC-5 `canonicalPools()`; `"launch"` = the token's own launch pool on a decoded launchpad, confirmed by that family's read), `ruleText`, `firstBlock`, `allIds` (all official pools; the first is the busiest). null = no official pool, so capture does not apply. |
| `venuePolicy` | object or null | URC-5 on the token: `read`, `declared`, `mode` (`Enforced` / `Preferred` / null), `canonicalPools` (count). |
| `shownPool` | object or null | The venue in the diagram. `selection`: `official` (token view: the official pool; the busiest one when there are several), `opened` (pool view: the pool in the URL), `busiestVenue24h` (token view, no official pool: the token's busiest venue by ~24 h token volume across every venue found, v4 with or without a hook, Abyss and other DEXs; method `research/FEE_CAPTURE.md` §1), `mainPoolRule` (no official pool and no trading on any venue found, or a snapshot made before 22 Oct: busiest active hooked pool). `shareOfTokenVolumePct`: its share of the token's volume in the capture window (null without capture). v4 pools: `id` (pool id), `official`, `uniswapV4` true, `hook`, `lpFee`, `pairedWith(Address)`, `createdAtBlock`, `swaps24`, `volume24` (amount). Abyss pools: `id` (pool contract), `official` false, `uniswapV4` false, `dex` `abyss`, `hook` null, `lpFee`, `pairedWith(Address)`, `swaps24`. Other DEXs: `id` (pool contract), `official` false, `uniswapV4` false, `dex` (`v3`, `v2`, `CL`, `Algebra`, `LB`, `Solidly`), `dexName` (factory name, e.g. "Uniswap v3"), `factory`, `hook` null, `lpFee`, `pairedWith(Address)`, `swaps24`, `volume24`. Hooked v4 pools with a small share are never shown here; the page names them on the capture line (minor = under 5% of the token's volume each). |
| `feesPerTrade` | object | `shown` / `basis` / `detail`: the diagram's source node ("Every trade pays", "2.30%", "hook fee on the ETH side …"); `buyTakePct`, `sellTakePct`: the take rate of the shown pool's hook (see `takeRate`); `lpFee`: the shown pool's LP fee. **Pons V2 and Cook add** (Bucks' fields): `totalPct` (hook fee + creator tax), `creatorPct`, `protocolPct`, `buybackPct`, `creatorTaxPct`, `shareOfFeeToCreatorPct` (creator share of everything the hook charges). |
| `split` | array or null | **The diagram's destinations, in order**, one object each: `label`; `share` (as shown, e.g. "1.50%", "0.2–1.0%", "?"); `sharePct` (number when `share` is a single percent); `of` (what the share is of: "of each trade", "of the tax", …); `wireShare` (this wire's fraction of the diagram's total width); `recipient` (first address the page names for it, or null when the recipient is not an address, e.g. "bought back and vested"); `recipients` (all addresses named in the destination's own text); `who` (the page's text for the destination, without the "Paid on" lines); `list` (per-wallet split when the page lists one: `{address, pct}`); `observed` (the page's "Observed" line for it, human amounts in the quote token, null when none); `notDecoded` (true for dashed "not decoded" wires); `paidOnTo` (when the page traced where one of those recipients pays on: `{address, text, chain, split, onwardSplitDecoded, onwardSplitBy}`; `text` = the "Paid on" line plus its onward-split lines; `chain` is the payee trace, hop by hop: `{addr, kind, via, name, …}`). null when the scan has no diagram (no v4 pool). |
| `split[].ens` | object or absent | Verified ENS primary names of this destination's addresses (`recipients` and `list`), `{address: name}`; the `who` text shows them as `name (address)`. See `ens`. |
| `split[].paidOnTo.chain[]` | array | The payee trace, hop by hop (what the page shows as "Paid on"). Each node: `addr`, `kind` (`wallet`, `7702`, `safe`, `contract`, `proxy`, `forwarder`, `unread`), `via` (the getter or slot that led here; `recipient` for the first), `from` (index of the node that led to it, -1 for the recipient), and when read: `name` (Sourcify), `size`, `impl`/`implName`/`padmin` (EIP-1967), `threshold`/`owners`/`ownerCount` (Safe), `delegate`/`delegateName` (EIP-7702), `gate` (address the code checks CALLER against), `roles` (AccessControl). **Names:** `ens` = the node's verified ENS primary name (see `ens`). **Deployers** (contracts verified on Sourcify): `deployTx` = creation transaction and `deploySender` = its sender (Sourcify v2 `deployment`), `deploySenderEns`; `deployerHow` = what the page established: `log` (the creation tx's receipt has a log emitted by a known deployer that names the contract), `tx.to` (the creation tx was sent to a known deployer), `constructor` (the receipt's `contractAddress` is a known deployer: created in its constructor), `direct` (the receipt's `contractAddress` is the contract itself: deployed by the sender), `sender` (the creation tx was sent by a known deployer wallet; the tx may have gone through other contracts, so `deployer` is not set), `unread` (receipt not read). `deployer` = the address that deployed it (`log`, `tx.to`, `constructor`: the known deployer; `direct`: the sender); `deployerName` and `deployerOf` = name and role from the reviewed table `app/known_deployers.json` (for `sender`: of the sending wallet). No `deployerHow` = the receipt matched none of these, the chain has no table entries, or the RPC cap was reached; `deploySender` alone is only the creation tx's sender. The same EIP-7702 delegate on two wallets says nothing about common ownership. |
| `split[].paidOnTo.split[]` | array | **Where that recipient's share goes next** (`payOnward`), one row per leg: `recipient` (address, or null when the code names a payee the trace did not read, e.g. ClausComputeFeeProgram's immutable `active()`); `role` (the page's wording for the leg, full addresses); `shareOfParentPct` (percent of the recipient's share); `shareOfTradePct` (percent of each trade = the destination's `sharePct` × `shareOfParentPct` / 100, only when the destination's share is one percent "of each trade", else null); `basis` = `read` (rate read from the chain at the scan's block) or `config` (share fixed in the contract's verified code; any recipient address in the row is still read at the scan's block); `beneficiary` (who can withdraw a staked leg, when the vault names one). Sources: a **fee-vault reader** on a chain node (`chain[].split`, below): OrbioAgentVault (four legs at vault-wide rates: treasury1, CREDIT to the agent wallet, gateway kept by the conversion wallet, staked for the agent) and OrbioStaking (two fixed halves in USDG: treasury1 and the conversion wallet, which backs CREDIT for ORBIO stakers); a Safe or EIP-7702 wallet as the recipient (one row, 100%, "held by …"); a reviewed contract (fixed legs from its verified source, `basis: config`). Empty when the onward split is described only in words or not decoded. |
| `split[].paidOnTo.onwardSplitDecoded` | boolean | true when the page shows where the recipient's share goes next: a vault reader's split read and matched to this recipient (OrbioAgentVault: `agentOf(agentIdOf(token)).receiver` = the traced recipient), a Safe / 7702 wallet holding it, a reviewed verified contract whose code fixes where it goes, or a family adapter that read the recipient's own split (Doppler/Rehype sub-hook). false when the recipient is a contract whose onward split is not decoded (unverified code, final shares off chain, or the vault's read did not match); the page then says it does not show who ends up with the funds, so the share is not to be read as the named creator's. `onwardSplitBy` = the reader's name (`OrbioAgentVault`, `OrbioStaking`), `family adapter`, `reviewed source`, `holder`, or null. |
| `split[].paidOnTo.chain[].split` | object or absent | A fee-vault reader's raw result on the vault's node (kept by snapshots): `reader`, `vault`, `block` (state read at), `ok`, `requests`, the values read (OrbioAgentVault: `agentId`, `nextAgentId`, `bps {treasury, credit, gateway, stake}`, `conversionBps`, `conversionWallet`, `treasury1`, `agent {token, receiver, owner, wallet}`, `creditTo` = `recipientOf(id)`, `receiverMatches`; OrbioStaking: `treasury1`, `conversionWallet`, `isRecipient`), `admin {address, hasRole}` (DEFAULT_ADMIN_ROLE checked for the proxy's Sourcify deployer only; other holders not read), `kinds {address: wallet\|7702\|contract}`, `legs [{key, bps, to}]`, `ens`, `error` when not read. At most eight vault-read stages (`PAY.cap.v`), additionally bounded by the global actual-transport budget. Treasury and conversion extensions are described below. |
| `feeRecipients` | object | Pons V2: `creator`, `protocol`, `buybackRecipient`, `buybackOn` (Bucks' fields), `escrow` (Pons fee escrow). Cook: `creator`, `protocol`, `owner`. Other families: absent (use `split`). |
| `feeSells` | object | Pons V2 / Cook only. `count` = `PoolFeesSwept` events, `avgMinutesBetween`, `hookSells`, `tokenSold`, `quoteReceived`. Cook retains its existing since-graduation scope. Pons totals apply only to `history`'s common completed interval across sweeps, hook swaps and recipient changes; no complete interval gives null totals. |
| `feeSells.history` | object | Pons only: `fromBlock`, `toBlock`, `requestedToBlock`, `complete`, `requests`, `reason` when limited/unavailable. `toBlock:null` means no complete interval, not zero activity. Optional enrichment is capped at 60 actual attempts/20 seconds with finishing reserves and runs after required core work. Old snapshots without this metadata remain unread. |
| `takeRate` | object or null | `{buyPct, sellPct}`: share of each trade the shown pool's hook routes to the project side (creator, platform, holders, burn, launchpad-owned liquidity), not to independent LPs (`research/FEE_CAPTURE.md` §3). null = not established for this family. |
| `feeCapture` | object or null | Last ~24 h, every venue found. Bucks' fields: `window` (`fromBlock`, `toBlock`, `approx`), `officialPoolShareOfTokenVolumePct`, `poolsFound` (v4 pools listing the token), `activePools` (v4 pools with swaps), `swaps` (all venues), `feesForgoneInQuote`, `feesForgoneUsd`, `venues`. Added: `quote` (`{symbol, address}` of the forgone amounts), `take` (the official pool's take used for forgone, `{buyPct, sellPct}`), `totalVolume` / `officialVolume` / `bypassVolume` (amounts), `officialSwaps`, `venuesWithSwaps`, `bypassVenues`, `venuesListed`, `quietV4Pools` (v4 pools with no swaps), `v4PoolsScanned`, `noHookFeeOver10PctVenues`, `officialPriceQuotePerToken`, `usdPerToken`, `usdSource` (`"official"` or the venue priced from), `ethUsd`, `otherDexScan` (`partial`, `fromBlock`, `toBlock`, `unattributedEmitters`, `unattributedSwaps`; null = that scan failed, so only v4 pools count), `requests`. null = capture did not run or failed. |
| `feeCapture.venues[]` | array | Official venue(s) first, then the 9 largest bypass venues by token volume (the same cut as the snapshots). Bucks' fields: `name`, `official`, `uniswapV4`, `hook` (v4 only), `lpFee` (`"dynamic"` = v4 dynamic-fee flag), `swaps`, `shareOfTokenVolumePct`. Added: `pool` (v4 pool id, or the pool contract for other DEXs), `dex` (`uniswap-v4`, `v3`, `v2`, `CL`, `Algebra`, `LB`, `Solidly`), `pairedWith(Address)`, `pair` (the pool's pair as the page shows it, `"TOKEN/QUOTE"`, e.g. `"CLAUS/USDC"`; the launched token comes first, so a `tokenIsOnlyTheQuote` venue reads `"OTHER/TOKEN"`; a side with no symbol on the page is given as its address), `volume` (amount), `feesForgoneInQuote`, `feesForgoneUsd` (null for official venues), `noHookFeeOver10Pct`, `tokenIsOnlyTheQuote` (another token's launch pool where this token is only the quote; counted as bypass). |
| `feeCapture.venues[].routedViaUniswap` | object or absent | Other-DEX venues only, `feeCapture` only (not `sinceLaunch`): present when a Uniswap aggregator wrapper routes into this pool (see the family note below). `wrappers` (`[{hook, poolId}]`), `swaps` (HookSwap events of those wrappers inside `window`; null = not counted, request cap), `venueSwaps` (the venue's own `swaps`, same window), `shareOfVenueSwapsPct` (`swaps / venueSwaps`), `protocolFeePips` (Uniswap protocol fee per routed swap: a number when every HookSwap in the window carried the same fee, `[min, max]` when not; with no routed swap, the wrapper's current fee as `[zeroForOne, oneForZero]`), `protocolFeePipsSource`, `tokenJar` (the hook's fee recipient), `protocolFeesCollected` (`[{currency, raw, human?}]`: ProtocolFeesCollected amounts matched to those swaps, per currency; `human` only when the page knows the decimals), `window` (`{fromBlock, toBlock}` = the other-DEX scan's covered range, so the share compares like with like), `counted`. |
| `feeCapture.uniswapAggregatorWrappers` | object or null | Every Uniswap aggregator wrapper pool that lists the token, set aside before the venue steps (`excludedFromVenues` true: not in `venues`, `poolsFound`, `activePools`, `v4PoolsScanned`, `lpPay`, `shownPool` or `officialPool`). `count`, `window`, `requests` / `requestCap` (≤ 10 per scan), `partial` (some wrapper not counted), `protocolFeeController` (the PoolManager's `protocolFeeController()`), `wrappers[]` (≤ 25): `poolId`, `hook`, `hookName`, `hookVersion` (`aggregatorHookVersion()`), `underlyingPool` (`poolIdToExternalPool`), `underlyingDex`, `underlyingInVenues`, `routedSwaps` (HookSwap count in the window; 0 without a read when the PoolManager logged no Swap for the wrapper), `poolManagerSwaps`, `protocolFeePipsNow` (`[zeroForOne, oneForZero]`), `tokenJar`, `createdAtBlock`. null = no wrapper (and in receipts made before 22 Oct). |
| `feeCapture.lpPay` | object, null or `{error}` | **What liquidity providers earned per $1 of liquidity** in the same ~24 h window (method: `research/FEE_CAPTURE.md` §LP pay), for the official pool(s) (up to 2) and the largest bypass venues the capture line names (up to 3). null = not measured (no official pool, or a snapshot made before 21 Oct ~23Z); `{error}` = the step failed. Fields: `window` (`fromBlock`, `toBlock`, `blocks` = toBlock − fromBlock + 1, `approx`), `stateBlock` (block of the state reads), `requests` / `requestCap` (≤ 40 extra requests per scan), `usdSource` (as `feeCapture.usdSource`), `method` (texts), `pools[]`. |
| `feeCapture.lpPay.pools[]` | array | One object per pool: `pool` (v4 pool id or pool contract), `pair`, `venue` (as the capture line names it), `official`, `hook` (v4 only), `lpFeeRate` (the pool's LP fee as shown: static rate, or for v4 dynamic fees the range charged in the window, "set per swap"), `lpFeeObservedPips` (v4: `[min, max]` LP fee in the window's Swap events, protocol fee taken out when one rate is set on both sides), `protocolShareNote` (what protocol share was taken out of LP fees, or that none was read), `lpShareOfFee` (fraction of the fee left to LPs after a non-v4 protocol share; 1 = none taken out), `lpFeesInWindow` (`quote` = LP fees in the pool's other currency, `quoteSymbol`, `usd`; `tokenEquivalent` instead of `usd` when no USD price), `hookLpShareInWindow` (null, or a hook's share paid to LPs in the window where the family reader measures it, **not included in `earningsPerDollar`**: `label`, `paidTo` (e.g. "staked LP positions only, not all LPs"), `quote`, `usd`, `perDollarOfAllPoolLiquidity`; today CLAUS staked-LP rewards and the LP part of Hookr arb recaptures), `liquidityValue` (`usd` or `tokenEquivalent`, `tokenAmount` and `otherAmount` (human units), `method` (text with blocks, event / tick counts), `block`, `activeLiquidityCheck` (v4: `source` = "ModifyLiquidity events" or "StateView tick profile", `inRange` = liquidity of the ranges holding the current tick, `stateViewLiquidity` = StateView `getLiquidity`, `match`, `tickRanges`, `events`+`fromBlock` or `initializedTicks`, `negativeRanges`)), `earningsPerDollar` (LP fees ÷ liquidity value, this window only; null when not valued), `notValued` (the reason, e.g. the request budget), `priceSource` (`spot` = the pool's sqrtPrice / reserves; `window average`), `windowBlocks`, `swaps`. |
| `sinceLaunch` | object or null | The page's "Measure since launch" run when it ran (snapshots made with `--since`): the same fields as `feeCapture` with `window.approx` = "since launch", plus `complete` (false = request cap ran out; `window.toBlock` is where it stopped; never extrapolated) and `v4PoolsSkipped`. If no complete range fit: `{fromBlock, toBlock: null, complete: false, requests}`. |
| `switches` | array or null | The page's "What can still change" rows, in order: `title`, `state` (the right-hand label as shown: who holds the lever, e.g. "Creator", "Token owner", or its state, e.g. "Fixed", "Locked", "On"), `level` (`ok` = cannot change / safe default, `warn` = someone can change it, `bad` = a lever that moves money or blocks trades), `text`, `addresses` (every address named, in order; for levers the holder comes first). null = not decoded for this family (generic hooks). |
| `ens` | object or null | The ENS lookup behind the names: `chain` 1, `coinType` 60 (the Ethereum mainnet address record, used for Robinhood Chain scans too: RH-specific primary names, coinType 0x80000000\|4663, are not read), `resolver` (ENS Universal Resolver `0xeeee…eeee`, `reverse(bytes,uint256)`: reverse record plus forward check, so a name is listed only if it resolves back to the same address), `block` (Ethereum block the names were read at), `pinned` (true = the scan's own block, Ethereum scans; false = the node's latest, Robinhood Chain scans), `node`, `requests` (≤2), `looked` (addresses asked), `names` (`{address: name}`), `offchain` (names that need CCIP-Read: not read), `mismatch` (reverse record set but the name does not resolve back), `unnormalized` (a name with characters outside lowercase a–z 0–9 - _: not shown), `unread`, `error`. null = no trace in this scan (or a snapshot made before names existed). |
| `activity` | object or null | Actual execution evidence: `asOfBlock`, chain `asOfTimestamp`, `maxLookbackSeconds`, `requestCap`, `softBudgetMs`, `requests`, `streams[]`. Null means the snapshot/reader did not collect this evidence. Maximum seven timestamp-bounded days, 64 actual attempts and 20 seconds, sharing global reserves. |
| `activity.streams[]` | object | `key`, `label`, exact `contract`, `kind`, `status` (`complete`, `partial`, `unread`), `requestedFromBlock`, contiguous observed `fromBlock`/`toBlock`, `count` (null until a range is read), `amounts[] {asset,decimals,raw,label}`, `last {block,tx,logIndex,timestamp}` or null, and `newerComplete`, `reason`, `timestampReason`, `state`, `note` when applicable. Empty bounded coverage is not “never”; last-event recency requires complete newer coverage. Deferred rewards are not delivered payments. |
| `vaultHistory` | array | Full-depth, dated cached Orbio evidence. Includes chain/address, `observedAt`, targeted block `coverage`/`throughBlock`, `upgrades`, historical `roles`, provenance, current implementation comparison and freshness limits. Three recorded post-deployment upgrades are not three split changes. `withheld` means cache knowledge postdates the snapshot or the scan clock is unavailable. Matching implementation does not prove unchanged settings or complete history. |
| `interpretation` | object | One pure presentation of the collected evidence, shared by page/full receipt: `kind`, `headline`, at most three `facts`, `qualification`, `limitCode`, `asOf`, `scope`, semantic `metrics`, and `economics` (below). It adds no provider reads, prediction or safety rating. |
| `interpretation.economics` | object or absent on older receipts | Exactly four sections: `funding`, `execution`, `beneficiaries`, `controls`. Each contains `{status, summary}`: evidence-coverage status and a concise complete answer. Compact consumers preserve all four sections when present; missing older evidence is not reconstructed. |
| `evidence` | object | `requests` (RPC requests the live scan made, all steps, the ENS lookup on Ethereum included; null for snapshots), `captureRequests`, `sinceRequests`, `rpc` (hosts the page reads for the scanned chain; the ENS host is `ens.node`). |
| `adapter` | object or null | The reader's own values as read (BigInt → decimal string, arrays cut at 25 items). Field names are the adapter's internals and can change between page versions; prefer the fields above. |
| `lookup` | object or absent | Only on receipts served by the paid lookup (`GET /undertow?token=…` on bucks-check; `app/lookup/`): `source` (`live` = scanned for this call, `cache` = a cached receipt the caller allowed with `max_age`, `sample`), `cache` (which one), `block`, `scannedAt`, `ageSeconds` (at serving time), `chainDetected` (true = the call gave no `chain` and the server picked it: Robinhood Chain first, then Ethereum, where the address has code; the receipt's `chain` says which), `scanSeconds` and `requests` (per host; live only), `engine` (`commit` of this repo, `page` = sha256 of the page that scanned, `jsdom` version). Everything else in the receipt is the page's `xrayReceipt()` unchanged. |

## Evidence-led interpretation

The page's top **Economic read**, full receipt and compact summary use the same `interpretation(M,fam)`; there is no second inference engine. The useful mechanism headline, strongest qualification and clock stay alongside the four answers:

| Section | Question answered |
|---|---|
| `funding` | What funds this mechanism, and how much observed trading reaches or bypasses the relevant fee path? |
| `execution` | What actually executed in the collected window, as distinct from configured routing, held funds or deferred payments? |
| `beneficiaries` | Who benefits, in which assets and under which decoded rights? Token ownership alone does not establish a payout or redemption right. |
| `controls` | Who can change the relevant rules, routing or implementation, and which control evidence remains unread? |

Every section has a `status` and `summary`. Status is **coverage, not a rating**: `known` means the stated answer is supported within its disclosed scope; `partial` means some relevant evidence is available but incomplete; `unread` means the answer was not established; `not-applicable` means that question does not apply to the identified mechanism. None means safe, solvent, guaranteed or comprehensively audited. Summaries aim for at most 240 characters without cutting off a qualification.

`asOf` carries the chain/head block, state-read blocks and latest-state fallback flag; its timestamp is null unless a matching execution block clock was actually collected. `scope` separates an opened pool from token-wide capture. Old snapshots and allowed cached receipts keep their original data/clock; reprojection of saved evidence is not a fresh scan. Missing observations never become zero, a pause flag or “never paid.” Later history is not backfilled into earlier snapshots.

Semantic metrics retain net/gross redemption and pending-escrow distinctions, held-balance capacity versus possible later claims, separate payout/buyback clocks, and agent-versus-token-holder rights. Capture preserves known unattributed swaps and denominator omissions. Temo's observed window average is not a fixed buy/sell setting; an unobserved fallback is not promoted into a decoded rate. A fixed recipient does not guarantee upstream funding. Independent activity windows must not be merged; configured, deferred and held amounts must not be counted as delivered.

`omittedDetails` reports bounded diagnostic excerpts; full underlying reader errors remain in their original evidence. Full receipt amounts preserve raw decimal strings, units/decimals, exact addresses, individual windows and reference provenance in the existing evidence fields; the concise interpretation does not replace them.

`format=summary` returns `undertow-summary/1` within 3,800 JSON characters at the same price. It projects the existing interpretation without reinference, protecting **all four economics sections (at least exact `status` + `summary`), strongest qualification, `asOf` and `scope`**, plus `by` attribution and `lookup.source`, `lookup.block`, `lookup.ageSeconds` and the original `lookup.scannedAt` (from the receipt's generator or lookup metadata). This core takes priority over duplicate verbose activity streams. `omitted` identifies dropped detail available in the full receipt. Older receipts without interpretation return `interpretation:null`; older interpretation objects without `economics` do not invent it. If unusually large or hostile input makes the protected core itself exceed the limit, the result explicitly reports `summary-core-too-large` instead of silently truncating decisive evidence.

## Treasury and compact financial state

`FeeFloor` keeps held ETH, pending escrow, six-decimal USDG trigger/value, batch remainder and processable clip separate. Its two 5,000-bps legs are operations and the GAGE purchase/floor mechanism. The supported floor is sGAGE/GAGE liquidity, not GAGE/ETH redemption. Missing or over-cap floor inventory is unread; `thresholdMet:false` is not a contract pause flag.

`UFGTreasury` includes `treasuryWei`, `effectiveSupplyRaw`, `quoteAmountRaw`, `grossQuoteWei`, `netQuoteWei`, `retainedExitFeeWei`, and pending escrow. One 10,000-bps incoming leg stays in the vault; the 200-bps exit retention is separate. `priceComparison` names the pool, state block, native-ETH units, rational sqrt-price calculation and separately scaled gross/net percentages. `scope` excludes executable proceeds, impact and guaranteed prices. Unsupported/failed price reads remain null, not a fixed historical percentage.

Orbio `conversion` separates lifetime `convertedOrbio`/`receivedUsdg`/`creditMinted`, pending ORBIO, and pinned wallet/vault USDG balances. Raw integers are decimal strings with asset decimals; a wallet balance is not the lifetime proceeds ledger or a statement of obligations.

The compact summary also projects `financialState`, `activity` and `vaultHistory` when space permits after the protected core. Detailed financial/activity records may be omitted atomically rather than cutting off decisive text or implying every raw financial field fits. Withheld history does not become zero upgrades. Full receipts retain the underlying chain-reader state without compact-summary truncation; field-specific bounds described above still apply.

## Family notes
**Fable** (`hook.adapter` `"fable"`, FableHook `0x376aa4c2e48ac2498caaac06bd7a6b6ef373f8e0`; 22 Oct). No new top-level fields: the split
and the switches come through `split[]` and `switches[]`. `split[]` rows are the coin's splitter destinations: `Creator` (absent while
the creator part is paid to the engine, as for FABLE), `Fable platform` (with `list` = the platform treasury's `destinations()`, shares of
the platform's share), `Burn vault`, `Mesh pot`; `sharePct` is percent of each trade from the splitter's getters (`PLATFORM_PIPS`,
`creatorPips`, `protocolPips`, `vaultBps`), `observed` carries the latest split event's measured vault / pot share of the engine part
(the pot can get less than its configured 30%). `takeRate` = the pool fee (`feePips/1e6`): the locker is every Fable pool's only LP.
Useful `adapter` values: `fee` (pips), `mesh`, `coin`, `C` (the factory's `coins(coin)` record: `sink`, `vault`, `builder`, `cBps`,
`pBps`), `sp` (splitter getters), `cActive` (`LiquidityLocker.creatorFeesActive`), `colA` / `colC` (fees collected from this pool,
raw, asset / coin side), `split.sum` (splitter payouts per currency since the pool opened, all of the coin's pools: creator, platform,
vault, pot), `split.last`, `burn` (`n` burn-vault transactions since the pool opened, `last`). The splitter, vault, pot, platform
treasury and guardian are not verified on Sourcify; see `research/families/fable/FABLE_FAMILY.md`.

**Uniswap aggregator wrappers** (`hook.adapter` `"aggregator"`, SlipstreamAggregator `0xa117…2888` and `0xa1ad…2888` on Robinhood Chain; 22 Oct;
`research/families/aggregator/AGGREGATOR_FAMILY.md`). A wrapper is a v4 pool whose hook runs every trade in another DEX's pool and adds a
Uniswap protocol fee. **Token view:** wrappers never appear in `shownPool`, `officialPool`, `feeCapture.venues`, the pool counts or
`lpPay`; the outside pool's venue row carries `routedViaUniswap`, and `feeCapture.uniswapAggregatorWrappers` lists them all. **Pool view of
a wrapper** (`#0xTOKEN/0xWRAPPERID`, `generator.view` `"pool"`): `hook` = `{family, address, callbacks, adapter: "aggregator", pool (the
wrapper's id), underlyingPool, underlyingDex, version, sourceVerified}`; `shownPool` = `{id, official: false, uniswapV4: true,
aggregatorWrapper: true, isVenue: false, hook, underlyingPool, underlyingDex, lpFee (the outside pool's fee() at the scan block),
protocolFeePips ([zeroForOne, oneForZero] = getSlot0 protocolFee × the hook's multiplier), pairedWith(Address), createdAtBlock, swaps24
(HookSwap count), poolManagerSwaps24, selection: "opened", shareOfTokenVolumePct: null}`; `feesPerTrade` adds `protocolFeePips`,
`protocolFeeMultiplier`, `lpFeeOf` (the outside pool); `split[]` = the outside pool's LPs (its LP fee) and `Uniswap protocol fee`
(recipient = the TokenJar, `observed` = routed swaps and fees in the window); `switches[]` = liquidity in the wrapper, outside pool, protocol
fee (who sets it, cap), fee recipient, hook code. `feeCapture` stays the token view's. Useful `adapter` values: `w` (the wrapper: `ext`,
`pf` raw getSlot0 protocolFee, `pm`, `hs`, `hits[]` = `{b, tx, li, tok, fee, cur, pfa}` per HookSwap, `fees` per currency), `H` (hook
getters), `ctrl`, `setter`, `policy`, `cOwner`, `pmOwner`, `rel`, `jOwner`, `ovr`, `impl`, `ext` (outside pool `fee`, `ts`, `factory`, `b0`/`b1`
balances).
## Differences from Bucks' Pons-only receipts (mva.json / orbio.json)
Same names and meanings for every field they had. Rounding is the same (percents 2 or 4 decimals, forgone 6 / 2). Bucks' `venues`
came from a trimmed snapshot (official + 9 bypass); the page applies the same cut. `officialPool` and `feeRecipients` gain fields; the
diagram (`split`), the switches, `blocks`, `generator`, `evidence`, amounts and `adapter` are new.
