ODIN LIQUIDITY NETWORK · ANALYTICS · AGENT DATA PLANE
machine-readable · live-first with labelled fallback · v1 · 2026-07-29 · companion to /odinagents
[ 0 · MANIFEST ]
ODIN ANALYTICS · AGENT DATA PLANE v1 (2026-07-29)
The network's own measurements, packaged for machines. This page is the
analytics counterpart of the agent surface at
https://odinliquidity.network/odinagents - that page holds the trust anchor
(canonical registry, verification procedures, constraints, corpus); this one
holds the DATA: every metric the odinOS instruments measure, categorized,
with three layers per metric:
1. #schema what the metric IS - category, unit, basis, constraint, freshness
2. #snapshot a dated, block-stamped value (the honest fallback, never live-dressed)
3. #recipes the exact endpoints + formulas that recompute it live
TWO CONSUMPTION MODES
static - you fetched this page as HTML: use #schema + #snapshot + #recipes.
Every recipe runs against open, keyless endpoints; nothing here
needs this page to be up.
browser - you drive a JavaScript context: this page fetches chain + market
state ON LOAD, prints the result into #live, and exposes
window.ODIN_ANALYTICS = {schema, snapshot, live, prov, refresh}.
Call refresh() for a fresh read - the heartbeat is yours.
RULES (inherited from /odinagents - they travel with every field)
- live | snapshot | implied are labelled and NEVER blended
- a metric without its constraint is a misquote; keys here match
/odinagents #constraints (apy, fees, lf, minLf, freeFloat, price, depth)
- if this page and the chain disagree, the chain is right
- freshness class "heartbeat" = recomputable now from open endpoints;
"snapshot-only" = needs measured history - the k SERIES IS PUBLISHED
(R9); the rest state their method so you can reproduce the measurement
SECTIONS: #manifest #schema #snapshot #recipes #live #livecode #selftest #boundaries
[ 1 · SCHEMA — THE METRIC CATALOG, CATEGORIZED ]
{
"version": 1, "categories": ["supply","pools","float","price","flow","depth","engine","diversification"],
"metrics": [
{ "id": "supply.max", "cat": "supply", "unit": "ODIN", "freshness": "constant", "def": "fixed maximum supply; totalSupply() always reads this - burns are held at addresses, never supply-reduced" },
{ "id": "supply.dead", "cat": "supply", "unit": "ODIN", "freshness": "heartbeat", "def": "ODIN at the dead address (the transferred burn)" },
{ "id": "supply.pvalhallaBurn", "cat": "supply", "unit": "ODIN", "freshness": "snapshot-only", "def": "ODIN accruing to the 33,333,333 pVAL locked at the dead address (the stranded burn); mechanism-derived from the pod ledger" },
{ "id": "supply.floors", "cat": "supply", "unit": "ODIN", "freshness": "heartbeat", "def": "sum of k/cap over the 10 fully-diluted pairs (the un-buyable burn)", "constraint": "floor" },
{ "id": "supply.burned", "cat": "supply", "unit": "ODIN", "freshness": "heartbeat", "def": "dead + pvalhallaBurn + floors - the canonical burn accounting" },
{ "id": "supply.circulating", "cat": "supply", "unit": "ODIN", "freshness": "heartbeat", "def": "max - burned" },
{ "id": "pools.reserves", "cat": "pools", "unit": "per-pool {oR,pR,k}", "freshness": "heartbeat", "def": "reserves of the 16 locked pools; sink pool oR is pVALHALLA, convert at engine.podIndex" },
{ "id": "pools.lockedLiquidityUsd","cat": "pools","unit": "USD", "freshness": "heartbeat", "def": "sum of locked-pool liquidity; dormant pools valued from chain reserves at price.odinUsd, labelled implied", "constraint": "provenance" },
{ "id": "pools.odinLocked", "cat": "pools", "unit": "ODIN", "freshness": "heartbeat", "def": "ODIN in the 16 pools: non-engine reserves + engine pVAL converted at podIndex, burned-stake share only" },
{ "id": "pools.dormant", "cat": "pools", "unit": "list", "freshness": "heartbeat", "def": "pools with no live market listing - DERIVED from feed absence, never a stored flag; quiet is not gone" },
{ "id": "float.floatBasis", "cat": "float", "unit": "ODIN", "freshness": "heartbeat", "def": "max - dead - pvalhallaBurn (the basis any where-does-it-sit split must total)", "constraint": "freeFloat" },
{ "id": "float.freeFloat", "cat": "float", "unit": "ODIN", "freshness": "heartbeat", "def": "floatBasis - odinLocked; NEVER circulating - odinLocked", "constraint": "freeFloat" },
{ "id": "float.lf", "cat": "float", "unit": "ratio","freshness": "heartbeat", "def": "2 x odinLocked / freeFloat - structural ownership gauge", "constraint": "lf" },
{ "id": "float.minLf", "cat": "float", "unit": "ratio","freshness": "heartbeat", "def": "2 x floors / circulating - the un-buyable base, a lower bound on LF, not on price", "constraint": "minLf" },
{ "id": "price.odinUsd", "cat": "price", "unit": "USD", "freshness": "heartbeat", "def": "MEDIAN across pools where ODIN is the base token - a network property, never one pair", "constraint": "price" },
{ "id": "price.watch", "cat": "price", "unit": "per-token USD", "freshness": "heartbeat", "def": "paired-token prices, each from its own deepest base-side market; pool-implied fallback labelled implied" },
{ "id": "flow.vol24hUsd", "cat": "flow", "unit": "USD", "freshness": "heartbeat", "def": "24h volume summed over locked pools with a live feed" },
{ "id": "flow.fees24hUsd", "cat": "flow", "unit": "USD", "freshness": "heartbeat", "def": "vol24 x 0.003 - the FULL V2 swap fee enters reserves; k measures exactly this. OVERSTATES on MEV-heavy days; trailing k-growth is the honest basis", "constraint": "fees" },
{ "id": "flow.feeApyKGrowthPct", "cat": "flow", "unit": "pct", "freshness": "snapshot-only", "def": "reserve compounding measured from trailing k-growth - the preferred APY basis", "constraint": "apy" },
{ "id": "flow.tvrRoutedUsd", "cat": "flow", "unit": "USD", "freshness": "snapshot-only", "def": "Total Value Routed: cumulative volume the locked pools processed, read from k alone" },
{ "id": "flow.tvrKeptUsd", "cat": "flow", "unit": "USD", "freshness": "snapshot-only", "def": "the fee residue of TVR that compounded into the reserves; kept/routed reconciles at the swap fee rate" },
{ "id": "depth.swapOut", "cat": "depth", "unit": "function", "freshness": "heartbeat", "def": "exact V2 output at 0.30 pct trader fee - closed form from reserves; see /odinagents #derivations", "constraint": "depth" },
{ "id": "depth.book", "cat": "depth", "unit": "ladder","freshness": "heartbeat", "def": "the 16 pools stacked into one ladder: for each price level, ODIN absorbable = sum over pools of x(1/sqrt(p)-...) closed form; recipe in #recipes", "constraint": "depth" },
{ "id": "engine.podIndex", "cat": "engine", "unit": "ODIN/pVAL", "freshness": "heartbeat", "def": "ODIN in pod / pVAL supply; ratchets up - a lower live read than a prior one is a bad read" },
{ "id": "engine.lockedShare", "cat": "engine", "unit": "fraction", "freshness": "heartbeat", "def": "burned spVAL / spVAL supply - the share of the sink pool that can never be unstaked" },
{ "id": "engine.pvalAtDeadPct", "cat": "engine", "unit": "pct", "freshness": "heartbeat", "def": "share of pVAL supply at the dead address" },
{ "id": "engine.stakerAprPct", "cat": "engine", "unit": "pct", "freshness": "snapshot-only", "def": "pod index growth annualized - ALWAYS name the window; whole-life figures embed the launch era", "constraint": "apy" },
{ "id": "engine.indexRatchet", "cat": "engine", "unit": "days", "freshness": "snapshot-only", "def": "measured consecutive days without an index decline" },
{ "id": "div.independentBets", "cat": "diversification", "unit": "count", "freshness": "snapshot-only", "def": "effective number of independent bets across the paired assets (eigenvalue method, external-venue prices - never pool-implied)" },
{ "id": "div.lpLegBets", "cat": "diversification", "unit": "count", "freshness": "snapshot-only", "def": "same measure across LP legs - lower because every leg is half ODIN" }
]
}
[ 2 · SNAPSHOT — DATED, BLOCK-STAMPED, NEVER LIVE-DRESSED ]
{
"meta": { "provenance": "snapshot", "date": "2026-07-22", "block": 25584535,
"rule": "The honest fallback. Heartbeat metrics: recompute via #recipes before relying. Snapshot-only metrics carry their own measurement date.",
"registryNote": "addresses and lock semantics live at /odinagents #registry - refuse any address not found there" },
"supply": { "max": 500000000, "dead": 312592185, "pvalhallaBurn": 34001794,
"floors": 2366886, "burned": 348960865, "burnedPct": 69.79, "circulating": 151039135,
"floorsByPool": { "SOJ": 2236811, "TSUKA": 64592, "OHMI": 53286, "PEAS": 5181, "WAIT": 4823, "DBI": 1798, "APU": 389, "APE": 4, "SHIB": 1, "LINK": 1 } },
"pools": { "lockedLiquidityUsd": 146300, "odinLocked": 59487093,
"sumReservesNonEngine": 53462539, "engineOdinLocked": 6024554,
"dormantAsOfSnapshot": ["SHIB","OHMI","SOJ","DBI"],
"reserves": [
{ "pair": "WETH", "oR": 8012830.74, "pR": 5.275363184 },
{ "pair": "TSUKA", "oR": 6833013.15, "pR": 9452882.009 },
{ "pair": "WBTC", "oR": 6973761.974, "pR": 0.13279741 },
{ "pair": "PEAS", "oR": 5798050.226, "pR": 8879.845445 },
{ "pair": "PAXG", "oR": 5221989.06, "pR": 1.629078527 },
{ "pair": "OHM", "oR": 5164972.036, "pR": 355.3902601 },
{ "pair": "DAI", "oR": 3970791.659, "pR": 5036.432422 },
{ "pair": "LINK", "oR": 2336313.985, "pR": 340.7258404 },
{ "pair": "APU", "oR": 1480063.738, "pR": 110427787.7 },
{ "pair": "APE", "oR": 682611.136, "pR": 5734.6947 },
{ "pair": "WAIT", "oR": 515441.1722, "pR": 935725.2234 },
{ "pair": "SHIB", "oR": 2028819.957, "pR": 599613828.8 },
{ "pair": "OHMI", "oR": 1487415.383, "pR": 3582460.122 },
{ "pair": "SOJ", "oR": 2572768.074, "pR": 0.8694181863 },
{ "pair": "DBI", "oR": 383696.9947, "pR": 9371152.644 },
{ "pair": "pVALHALLA/pOHM", "engine": true, "oR": 6206246.078, "pR": 397.629076 } ] },
"float": { "floatBasis": 153406021, "freeFloat": 93918928, "lf": 1.2668, "minLf": 0.0313 },
"price": { "odinUsd": 0.00126353 },
"flow": { "vol24hUsd": 6305, "fees24hUsdMeasured": 4.95,
"flowNote": "measured fee income (metrics DB) does not reconcile with vol24 x any flat rate - fee income is k-growth-measured while volume is feed-reported. One more reason the k-growth basis wins.",
"feeApyKGrowthPct": { "value": 4, "approx": true, "window": "trailing, measured 2026-07-27", "note": "the same day, vol24-annualizing read 25 pct - the MEV-day trap" },
"tvrRoutedUsd": { "value": 19074957, "asOf": "2026-07-28", "method": "read from k alone - see #recipes" },
"tvrKeptUsd": { "value": 57224.87, "asOf": "2026-07-28" } },
"engine": { "podIndex": 1.0200538508886943, "lockedShare": 0.9516402154933289,
"pvalAtDeadPct": { "value": 52.08, "asOf": "2026-07-29" },
"stakerAprPct": { "value": 0.283, "approx": true, "window": "trailing-year basis, measured 2026-07-29", "note": "the whole-life figure (about 1.04 pct) embeds the launch window - always name the window" },
"indexRatchet": { "days": 702, "declines": 0, "asOf": "2026-07-29" } },
"diversification": { "independentBets": { "value": 2.75, "assets": 15, "asOf": "2026-07-28" },
"lpLegBets": { "value": 1.13, "asOf": "2026-07-28", "note": "every LP leg is half ODIN, so legs co-move" },
"basis": "external-venue daily prices, never pool-implied; eigenvalue effective-bets method" }
}
[ 3 · RECIPES — RECOMPUTE ANY HEARTBEAT METRIC, NOW ]
Open, keyless endpoints. Selectors + canonical addresses: /odinagents #registry.
Formulas: /odinagents #derivations (this page embeds the same ones in
#livecode and runs them on itself in a browser context).
R1 · RESERVES + PRICE + VOLUME (one batch)
GET https://api.dexscreener.com/token-pairs/v1/ethereum/{ODIN}
For each returned pair whose pairAddress is in the locked registry:
liquidity.base/quote are the reserves (base = the ODIN side when
baseToken.symbol is ODIN), volume.h24 is the pool's 24h volume, priceUsd is
that pool's ODIN print. price.odinUsd = MEDIAN over ODIN-base prints only.
A registry pool ABSENT from the response is dormant (derived, not stored):
read its reserves on-chain via getReserves() and value it at the network
price, labelled implied. Trust dormancy re-derivation only when the feed
answered for a majority of the registry - a thin response means "could not
see", never "went quiet".
R2 · THE BURN, LIVE
dead = balanceOf(DEAD) at ODIN (guard: within 10 pct of snapshot -
this quantity moves slowly; a wild jump is a decode error)
floors = sum over capped pairs of (oR x pR) / totalSupply(pairedToken)
(re-read caps live - PEAS burns, so its floor RISES; guard:
a cap jumping UP or halving is a bad proxy/decimals read)
pvalhallaBurn stays snapshot (mechanism-derived from the pod ledger)
burned = dead + pvalhallaBurn + floors ; circulating = max - burned
R3 · FLOAT + LF
odinLocked = sum(non-engine oR) + engine.pvalReserve x podIndex x lockedShare
floatBasis = max - dead - pvalhallaBurn
freeFloat = floatBasis - odinLocked (NEVER circulating - odinLocked)
lf = 2 x odinLocked / freeFloat ; minLf = 2 x floors / circulating
R4 · THE SINK
podIndex = balanceOf(ODIN at PVAL) / totalSupply(PVAL) - must be at
least 1 and never below a previously accepted live read
lockedShare = balanceOf(SPVAL at DEAD) / totalSupply(SPVAL)
pvalAtDeadPct = balanceOf(PVAL at DEAD) / totalSupply(PVAL) x 100
R5 · DEPTH / BOOK (pure arithmetic, zero fetches beyond R1)
swapOut(a, rIn, rOut) = a x 997 x rOut / (rIn x 1000 + a x 997)
Ladder: to move a pool's price to fraction f of spot (f below 1, selling),
ODIN absorbed by that pool = oR x (1/sqrt(f) - 1) before fees; stack all 16
(sink pool excluded - its book is pod-denominated). The MODELLED ladder;
the MEASURED route is a live quote (CoW Swap /api/v1/quote, keyless).
R6 · SNAPSHOT-ONLY METHODS (reproduce the measurement, not just the number)
feeApyKGrowthPct: k ratchets only from fees, so growth of sqrt(k) between
two blocks IS the compounded fee income - annualize that, never vol24.
TVR: cumulative routed volume recovered from the k series alone:
each day's fee growth implies the volume that paid it at the fee rate;
kept/routed must reconcile at the swap fee rate (assert it).
stakerAprPct: pod index series, growth over a NAMED trailing window.
independentBets: daily external-venue returns matrix, correlation
eigenvalues, effective bets = (sum lambda)^2 / sum(lambda^2).
R7 · ONE CALL, THE WHOLE CANONICAL STATE (Multicall3)
Multicall3 0xcA11bde05977b3631167028862bE2a173976CA11 (same address on
every chain), method aggregate3((address,bool,bytes)[]), selector
0x82ad56cb, allowFailure true on each call. The canonical read:
getReserves() 0x0902f1ac at each of the 16 registry pools
balanceOf(DEAD) 0x70a08231+pad32(DEAD) at ODIN, at PVAL, at SPVAL
balanceOf(PVAL) 0x70a08231+pad32(PVAL) at ODIN (the pod position)
totalSupply() 0x18160ddd at PVAL, at SPVAL,
at the 10 capped paired tokens
= 32 calls, ONE eth_call, one block: a same-block atomic snapshot, which
the per-call road cannot guarantee. Decode getReserves as
(uint112 r0, uint112 r1, uint32 ts); ODIN is token1 in all 15 ODIN pools.
Feed the decoded values to R2-R4 exactly as written.
R8 · GET-ONLY SANDBOX - RESERVES WITHOUT eth_call
Some agent sandboxes can only GET. Explorer GET APIs expose balanceOf,
and for a synced V2 pair balanceOf(token, pairAddress) EQUALS that
token's reserve (sync() writes balances into reserves; a donation before
sync overstates briefly - cross-check two sources when it matters).
DexScreener (R1) is also plain GET. R2-R5 are then pure arithmetic.
R9 · THE k SERIES (the recorded prior state that k-growth metrics need)
Daily append-only history since genesis (2023-02-06), block-stamped, one
row per day: per-pool k, floors, dead balance, podIndex, lockedShare.
FILE k_history_v1.json - published as a CORS-fetchable asset; its URL is
on the trust anchor page (/odinagents, CANONICAL URLS); verify by
sha256 10d4796c5d79008b3f91b2c9ae1d11f7e70ad111d2975025770169fe126f4eda
Columns, caps (FULL precision), and the cap-history caveat are in-file.
Trailing k-growth over a window = sqrt(k_end / k_start) - 1 per pool:
that IS the compounded fee return (R6). History never changes - reserves
at a past block are immutable facts; regeneration only appends rows.
[ 4 · LIVE — WRITTEN BY THIS PAGE AT LOAD, IN A BROWSER CONTEXT ]
{ "provenance": "static copy - no live read has run. Fetch-mode consumers: use
#snapshot + #recipes. Browser-mode consumers: this block is rewritten with a
fresh read at load; window.ODIN_ANALYTICS.refresh() re-reads on demand." }
[ 5 · LIVE CODE — THE FETCH LAYER, DISPLAYED = EXECUTED ]
/* The live layer. The runner compiles THIS displayed source and executes it -
there is no hidden second copy. Guards mirror the odinOS data hub: a bad
read keeps the labelled snapshot, never publishes a wrong number.
Addresses duplicated here from /odinagents #registry for self-containment;
that page remains canonical. */
var LIVE = {};
LIVE.run = function(SNAP){
var ODIN = "0xDfC5964141c018485b4d017634660f85aa667714";
var PVAL = "0xA77013378dAc318802dddF40bD7C7EF5dB594c95";
var SPVAL = "0x80517Ad00bb79079DAdf009a9F6c6B8534F2fb80";
var DEAD = "0x000000000000000000000000000000000000dEaD";
var POOLS = [
{ pair: "WETH", addr: "0x01dbAa18e86F65DF04cE9F897d2cAb22Ef0c26CB", tok: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" },
{ pair: "TSUKA", addr: "0xc0376b0Cbc8E92BCC21e6B9a2f1a2351E714C6E6", tok: "0xc5fB36dd2fb59d3B98dEfF88425a3F425Ee469eD", snapCap: 1000000000 },
{ pair: "WBTC", addr: "0xAd673E520Aada71CF73CD8e257A5d50c78EB01a0", tok: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" },
{ pair: "PEAS", addr: "0xBaF9705037c14ad072A0DAF87Cd113de1A2A2F51", tok: "0x02f92800F57BCD74066F5709F1Daa1A4302Df875", snapCap: 9937294.670594584 },
{ pair: "PAXG", addr: "0x2808F174244D17F1878ec03fd111222A4E4e4892", tok: "0x45804880De22913dAFE09f4980848ECE6EcbAf78" },
{ pair: "OHM", addr: "0x08B30F89b3152E9815c79D8cf59eF8425693B08d", tok: "0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5" },
{ pair: "DAI", addr: "0x6c027635A22FaaF29b9F565D543f91236c1864FE", tok: "0x6B175474E89094C44Da98b954EedeAC495271d0F" },
{ pair: "LINK", addr: "0x62cB46A15589aC8712610AF26235Bfe99B9f553f", tok: "0x514910771AF9Ca656af840dff83E8264EcF986CA", snapCap: 1000000000 },
{ pair: "APU", addr: "0x37C7Fde50966F36c3c5ff3379f817bf6A8C9745C", tok: "0x594DaaD7D77592a2b97b725A7AD59D7E188b5bFa", snapCap: 420690000000 },
{ pair: "APE", addr: "0xFA7ea3c400115f67690E8b21Bfec5Da8C539a18D", tok: "0x4d224452801ACEd8B2F0aebE155379bb5D594381", snapCap: 1000000000 },
{ pair: "WAIT", addr: "0x348B9E201641a10A824Fc607383Ba2B455C23D79", tok: "0x2559813BBb508C4C79e9CCCE4703Bcb1f149EDd7", snapCap: 100000000 },
{ pair: "SHIB", addr: "0x3FeB0845e90F49c010C7704e44af829ACB9e61d1", tok: "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE", snapCap: 999982329478394 },
{ pair: "OHMI", addr: "0xdDf33339f86CcD48fFEd72b0F22C6e9669b1e2AB", tok: "0x4159862bcf6b4393a80550b1ed03dffa6f90533c", snapCap: 100000000 },
{ pair: "SOJ", addr: "0x440944d4f7b7A32349724612b757a1d5f1E624B9", tok: "0x5830d25d257e46084361f8dc5e758736fd45a27a", snapCap: 1 },
{ pair: "DBI", addr: "0x97EEd004224B6c68B6752D89207CaB28c8fbD09e", tok: "0x2de509bf0014ddf697b220be628213034d320ece", snapCap: 2000000000 },
{ pair: "ENGINE", engine: true, addr: "0x4Bcd9c0397401232eA65F6790aA8a3837FdBC8B1" }
];
var RPCS = ["https://eth.drpc.org", "https://ethereum-rpc.publicnode.com", "https://cloudflare-eth.com"];
var errors = [];
function rpc(to, data){
function attempt(i){
if(i >= RPCS.length) return Promise.reject(new Error("all rpcs failed"));
return fetch(RPCS[i], { method: "POST", headers: { "content-type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_call", params: [{ to: to, data: data }, "latest"] }) })
.then(function(r){ if(!r.ok) throw 0; return r.json(); })
.then(function(j){ if(j.error) throw 0; if(j.result == null) throw 0; return j.result; })
.catch(function(){ return attempt(i + 1); });
}
return attempt(0);
}
function pad(a){ return "000000000000000000000000" + a.slice(2).toLowerCase(); }
function bal(token, holder){ return rpc(token, "0x70a08231" + pad(holder)).then(function(h){ return parseInt(h, 16) / 1e18; }); }
function sup18(token){ return rpc(token, "0x18160ddd").then(function(h){ return parseInt(h, 16) / 1e18; }); }
function capOf(p){
return Promise.all([rpc(p.tok, "0x18160ddd"), rpc(p.tok, "0x313ce567")]).then(function(r){
var d = parseInt(r[1], 16);
var v = Number(BigInt(r[0])) / Math.pow(10, d);
if(!isFinite(v)) throw 0;
if(!(v > 0)) throw 0;
if(v > p.snapCap * 1.0001) throw 0; /* caps never jump up */
if(p.snapCap > 2 * v) throw 0; /* nor halve - bad decimals read */
return v;
}).catch(function(){ errors.push("cap:" + p.pair + " snapshot kept"); return p.snapCap; });
}
function feed(){
return fetch("https://api.dexscreener.com/token-pairs/v1/ethereum/" + ODIN)
.then(function(r){ if(!r.ok) throw 0; return r.json(); })
.then(function(j){ return Array.isArray(j) ? j : (j.pairs ? j.pairs : []); });
}
var snapReserves = {};
SNAP.pools.reserves.forEach(function(p){ snapReserves[p.pair] = p; });
var out = { at: new Date().toISOString(), provenance: {}, errors: errors };
return Promise.all([
feed().catch(function(){ errors.push("feed unreachable"); return null; }),
bal(ODIN, DEAD).catch(function(){ return null; }),
Promise.all(POOLS.filter(function(p){ return p.snapCap > 0; }).map(function(p){
return capOf(p).then(function(v){ return { pair: p.pair, cap: v }; }); })),
Promise.all([bal(ODIN, PVAL), sup18(PVAL), bal(SPVAL, DEAD), sup18(SPVAL), bal(PVAL, DEAD)]).catch(function(){ return null; }),
rpc(POOLS[15].addr, "0x0902f1ac").catch(function(){ return null; })
]).then(function(res){
var pairsRaw = res[0], deadLive = res[1], caps = res[2], pod = res[3], engRes = res[4];
/* pools: live reserves where the feed answered; dormancy re-derived only
when a majority answered ("could not see" must never read as "quiet") */
var map = {};
if(pairsRaw){ pairsRaw.forEach(function(d){ if(d){ if(d.pairAddress) map[d.pairAddress.toLowerCase()] = d; } }); }
var nonEngine = POOLS.filter(function(p){ return !p.engine; });
var seen = nonEngine.filter(function(p){ return map[p.addr.toLowerCase()]; }).length;
var trustDorm = seen * 2 > nonEngine.length;
var prints = [], vol24 = 0, volKnown = false, dormant = [], reserves = [], liqUsd = 0;
nonEngine.forEach(function(p){
var d = map[p.addr.toLowerCase()], s = snapReserves[p.pair];
var row = { pair: p.pair, oR: s.oR, pR: s.pR, prov: "snapshot" };
if(d){
var bs = String(d.baseToken ? d.baseToken.symbol : "").toUpperCase();
var odinBase = bs === "ODIN";
if(d.liquidity){
if(d.liquidity.base > 0){ if(d.liquidity.quote > 0){
row.oR = odinBase ? d.liquidity.base : d.liquidity.quote;
row.pR = odinBase ? d.liquidity.quote : d.liquidity.base;
row.prov = "live"; } }
if(d.liquidity.usd > 0){ row.liqUsd = d.liquidity.usd; liqUsd += d.liquidity.usd; }
}
if(d.volume){ if(isFinite(+d.volume.h24)){ vol24 += +d.volume.h24; volKnown = true; } }
if(odinBase){ if(+d.priceUsd > 0) prints.push(+d.priceUsd); }
} else if(trustDorm){ dormant.push(p.pair); }
row.k = row.oR * row.pR;
reserves.push(row);
});
prints.sort(function(a, b){ return a - b; });
var price = prints.length ? prints[Math.floor(prints.length / 2)] : SNAP.price.odinUsd;
out.price = { odinUsd: price, prints: prints.length };
out.provenance.price = prints.length ? "live median of " + prints.length + " base-side prints" : "snapshot";
/* dormant pools: value from chain reserves at the network price, implied */
dormant.forEach(function(pair){
var row = reserves.filter(function(r){ return r.pair === pair; })[0];
row.liqUsd = 2 * row.oR * price; row.prov = row.prov + " · implied-value";
liqUsd += row.liqUsd;
});
/* the burn */
var dead = SNAP.supply.dead, deadProv = "snapshot";
if(deadLive){ if(deadLive > 0){
if(Math.abs(deadLive - SNAP.supply.dead) / SNAP.supply.dead > 0.10){ errors.push("dead read outside guard - snapshot kept"); }
else { dead = deadLive; deadProv = "live"; } } }
var capMap = {}; caps.forEach(function(c){ capMap[c.pair] = c.cap; });
var capsLive = caps.length - errors.filter(function(e){ return e.indexOf("cap:") === 0; }).length;
var floors = 0, floorsByPool = {};
reserves.forEach(function(r){
var c = capMap[r.pair];
if(c > 0){ var f = r.k / c; floors += f; floorsByPool[r.pair] = Math.round(f); }
});
var burned = dead + SNAP.supply.pvalhallaBurn + floors;
var circulating = SNAP.supply.max - burned;
out.supply = { max: SNAP.supply.max, dead: dead, pvalhallaBurn: SNAP.supply.pvalhallaBurn,
floors: Math.round(floors), floorsByPool: floorsByPool, burned: Math.round(burned),
burnedPct: Math.round(10000 * burned / SNAP.supply.max) / 100, circulating: Math.round(circulating) };
out.provenance.supply = deadProv + " dead · snapshot pvalhallaBurn · floors from " + capsLive + " of " + caps.length + " live caps";
/* the engine */
var podIndex = SNAP.engine.podIndex, lockedShare = SNAP.engine.lockedShare,
pvalAtDeadPct = SNAP.engine.pvalAtDeadPct.value, engProv = "snapshot";
if(pod){
var i0 = pod[0] / pod[1], sh = pod[2] / pod[3];
var ok = true;
if(!(i0 > 0.5)) ok = false;
if(i0 > 5) ok = false;
if(!(sh > 0)) ok = false;
if(sh > 1) ok = false;
if(SNAP.engine.podIndex > i0 + 1e-9) ok = false; /* the ratchet: live must not sit below the past */
if(ok){ podIndex = i0; lockedShare = sh; pvalAtDeadPct = Math.round(10000 * pod[4] / pod[1]) / 100; engProv = "live"; }
else { errors.push("pod read failed guards - snapshot kept"); }
}
out.engine = { podIndex: podIndex, lockedShare: lockedShare, pvalAtDeadPct: pvalAtDeadPct };
out.provenance.engine = engProv;
/* sink pool reserves: token0 is pOHM (lower address), token1 is pVALHALLA */
var engPvR = snapReserves["pVALHALLA/pOHM"].oR, engProv2 = "snapshot";
if(engRes){
var r0 = parseInt(engRes.slice(2, 66), 16) / 1e18, r1 = parseInt(engRes.slice(66, 130), 16) / 1e18;
if(r1 > 0){ engPvR = r1; engProv2 = "live"; }
}
reserves.push({ pair: "pVALHALLA/pOHM", engine: true, pvR: engPvR, prov: engProv2 });
/* float + LF */
var odinLocked = 0;
reserves.forEach(function(r){
if(r.engine){ odinLocked += r.pvR * podIndex * lockedShare; } else { odinLocked += r.oR; }
});
var floatBasis = SNAP.supply.max - dead - SNAP.supply.pvalhallaBurn;
var freeFloat = floatBasis - odinLocked;
out.pools = { odinLocked: Math.round(odinLocked), lockedLiquidityUsd: Math.round(liqUsd),
dormant: trustDorm ? dormant : SNAP.pools.dormantAsOfSnapshot, reserves: reserves };
out.provenance.pools = pairsRaw ? ("feed live · " + seen + " of " + nonEngine.length + " listed") : "snapshot";
out.float = { floatBasis: Math.round(floatBasis), freeFloat: Math.round(freeFloat),
lf: Math.round(10000 * 2 * odinLocked / freeFloat) / 10000,
minLf: Math.round(10000 * 2 * floors / circulating) / 10000 };
out.provenance.float = "derived from the reads above - identities hold by construction";
out.flow = volKnown
? { vol24hUsd: Math.round(vol24), fees24hUsd: Math.round(100 * vol24 * 0.003) / 100,
basis: "vol24 x 0.003 - the full V2 swap fee enters reserves, which is what k measures and what the TVR test above reconciles at. Overstates on MEV-heavy days; trailing k-growth is the honest basis" }
: { vol24hUsd: null, note: "no live volume - nothing fabricated" };
out.provenance.flow = volKnown ? "live" : "unavailable";
return out;
});
};
[ 6 · SELF-TEST — THE CATALOG PROVES ITSELF ]
/* Runs at load against the displayed #schema and #snapshot. Identities and
reconciliations, not just values - a catalog whose own numbers do not
reconcile is not a data plane, it is a brochure. */
function runSelfTest(SCHEMA, SNAP){
var R = [];
function t(name, pass, detail){ R.push({ name: name, pass: !!pass, detail: detail }); }
/* catalog shape */
var ids = {}; var dup = false;
SCHEMA.metrics.forEach(function(m){ if(ids[m.id]) dup = true; ids[m.id] = m; });
t("schema: metric ids unique", !dup, String(SCHEMA.metrics.length) + " metrics");
var catSet = {};
SCHEMA.metrics.forEach(function(m){ catSet[m.cat] = 1; });
t("schema: every declared category is populated",
SCHEMA.categories.every(function(c){ return catSet[c] === 1; }), SCHEMA.categories.join(","));
var fresh = SCHEMA.metrics.every(function(m){
if(m.freshness === "heartbeat") return true;
if(m.freshness === "snapshot-only") return true;
return m.freshness === "constant";
});
t("schema: freshness class is always declared", fresh, "");
/* snapshot-only metrics must carry their own measurement date */
var dated = [SNAP.flow.feeApyKGrowthPct.window, SNAP.flow.tvrRoutedUsd.asOf,
SNAP.engine.stakerAprPct.window, SNAP.engine.indexRatchet.asOf,
SNAP.diversification.independentBets.asOf].every(function(x){ return typeof x === "string"; });
t("snapshot: every snapshot-only metric names its date or window", dated, "");
/* burn accounting reconciles from its own parts */
var floorsSum = 0;
Object.keys(SNAP.supply.floorsByPool).forEach(function(k){ floorsSum += SNAP.supply.floorsByPool[k]; });
t("supply: floorsByPool sums to floors", 3 > Math.abs(floorsSum - SNAP.supply.floors), String(floorsSum));
t("supply: SOJ dominates the floors (94.5 pct)",
0.2 > Math.abs(100 * SNAP.supply.floorsByPool.SOJ / SNAP.supply.floors - 94.5), "");
var burned = SNAP.supply.dead + SNAP.supply.pvalhallaBurn + SNAP.supply.floors;
t("supply: dead + pvalhallaBurn + floors = burned", 1 > Math.abs(burned - SNAP.supply.burned), "");
t("supply: circulating = max - burned",
1 > Math.abs(SNAP.supply.max - burned - SNAP.supply.circulating), "");
/* reserves reproduce the aggregates */
var sumOR = 0;
SNAP.pools.reserves.forEach(function(p){ if(!p.engine) sumOR += p.oR; });
t("pools: reserve sum reproduces sumReservesNonEngine", 2 > Math.abs(sumOR - SNAP.pools.sumReservesNonEngine), sumOR.toFixed(0));
var eng = SNAP.pools.reserves.filter(function(p){ return p.engine; })[0];
var engLocked = eng.oR * SNAP.engine.podIndex * SNAP.engine.lockedShare;
t("pools: engine locked = pvR x podIndex x lockedShare", 2 > Math.abs(engLocked - SNAP.pools.engineOdinLocked), engLocked.toFixed(0));
t("pools: odinLocked = reserves + engine share",
3 > Math.abs(sumOR + engLocked - SNAP.pools.odinLocked), "");
/* the float identities */
t("IDENTITY: floatBasis = max - dead - pvalhallaBurn",
1 > Math.abs(SNAP.supply.max - SNAP.supply.dead - SNAP.supply.pvalhallaBurn - SNAP.float.floatBasis), "");
t("IDENTITY: floatBasis = odinLocked + freeFloat",
3 > Math.abs(SNAP.float.floatBasis - SNAP.pools.odinLocked - SNAP.float.freeFloat), "");
t("float: lf recomputes", 0.0005 > Math.abs(2 * SNAP.pools.odinLocked / SNAP.float.freeFloat - SNAP.float.lf), "");
t("float: minLf recomputes", 0.0005 > Math.abs(2 * SNAP.supply.floors / SNAP.supply.circulating - SNAP.float.minLf), "");
/* TVR reconciles at the swap fee rate - kept IS the fee residue of routed */
var ratio = SNAP.flow.tvrKeptUsd.value / SNAP.flow.tvrRoutedUsd.value;
t("flow: TVR kept/routed reconciles at the 0.30 pct swap fee",
(function(){ if(ratio > 0.0029){ return 0.0031 > ratio; } return false; })(), ratio.toFixed(5));
/* basis discipline is stated, not implied */
t("flow: k-growth basis carries its window",
SNAP.flow.feeApyKGrowthPct.window.indexOf("trailing") === 0, SNAP.flow.feeApyKGrowthPct.window);
t("engine: staker APR names its window",
SNAP.engine.stakerAprPct.window.indexOf("trailing") === 0, SNAP.engine.stakerAprPct.window);
t("engine: index ratchet is unbroken", SNAP.engine.indexRatchet.declines === 0,
SNAP.engine.indexRatchet.days + " days");
t("engine: pod index above 1", SNAP.engine.podIndex > 1, "");
/* diversification sanity: bets bounded by asset count, legs below assets */
var b = SNAP.diversification.independentBets;
t("div: independent bets within (1, assets)",
(function(){ if(b.value > 1){ return b.assets > b.value; } return false; })(), String(b.value));
t("div: LP legs measure below the asset measure",
b.value > SNAP.diversification.lpLegBets.value, String(SNAP.diversification.lpLegBets.value));
return R;
}
self-test pending - enable JavaScript, or run the same checks yourself: that is the point.
[ 7 · BOUNDARIES ]
READ-ONLY, PREDICTION-FREE, PERSON-FREE, ADVICE-FREE, CHAIN-FIRST - the full
statements live at /odinagents #boundaries and bind this page identically.
Specific to this plane:
- NOTHING IS FABRICATED. A metric whose read fails keeps its labelled
snapshot; a metric with no honest source reads null with a note. An
empty field is a measurement too.
- THE HEARTBEAT IS YOURS. This page fetches once at load. Poll by calling
window.ODIN_ANALYTICS.refresh(), or run #recipes on your own schedule -
the endpoints are open and keyless precisely so you do not need us.
- SNAPSHOT-ONLY METRICS are measurements from the network's history
archive, dated. Reproduce them via the methods in R6 before extending
them; do not extrapolate them forward.
- INTEGRITY: confirm the ODIN address in #livecode matches /odinagents
#registry; run the self-test; on any failure trust the chain, not this page.