The feed

Price sources

Each source is a tick producer feeding one scriptable publisher. Add a source and it inherits history, the σ window, calc & guard scripts, and the sanity guard for free.

Exchange & Lazer

binance · bybit

Top-of-book best bid/ask over WebSocket → mid = (bid+ask)/2, confidence = half-spread, as a fixed-point mantissa. Dashboard-managed feed sets.

lazer

Pyth Lazer: one WS multiplexes subscriptions; publishes raw prices and, for evm subscriptions, the signed blob the pyth pusher forwards.

binance_futures

Perp mids into a shared series the CEX calc reads as etx_perp_ret_2s/10s — the toxic-flow signal. Inert when unconfigured.

On-chain sources

Two generic, config-driven sources put chain data onto the same bus. Anything past a linear scale — inversion, cross rates, sqrtPriceX96 — belongs in a calc script, not the source config.

evm_events — contract logs

eth_subscribe("logs") on the shared WS runner, or eth_getLogs polling from the last seen block. A feed names the emitting address, the event signature (with indexed markers), the price field and a scale.

[[evm_events]]
name   = "base-events"
ws_url = "wss://…"          # or http_url = "https://…" for getLogs polling

[[evm_events.feeds]]
symbol      = "eth"
address     = "0x…"
event       = "AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt)"
price_field = "current"      # named param — must be uint*/int*
scale       = 1e-8            # real_price = decoded * scale

evm_calls — Multicall3 batch

One eth_call per poll to Multicall3 aggregate3, batching every configured zero-arg view call. allowFailure is set per call, so a reverting target skips only its own symbol — never the batch.

[[evm_calls]]
name    = "base-calls"
rpc_url = "https://…"          # multicall3 defaults to the canonical address

[[evm_calls.feeds]]
symbol      = "steth"
address     = "0x…"
call        = "latestRoundData()"   # zero-arg; use `calldata = "0x…"` for args
returns     = "uint80,int256,uint256,uint256,uint80"
price_field = 1                   # tuple index — must be uint*/int*
scale       = 1e-8
The ABI helpers (event topic0, zero-arg selectors, static-word decode, the aggregate3 shape) are hand-rolled in Rust and mirrored on ethers v6 in the twin — with both test suites pinning the identical hex vectors, so the encoders are proven equivalent.

What every source publishes

A tick becomes {prefix}:{name}:{symbol} with the price mantissa, exponent, the script's confidence and spreads, and a history entry. On-chain sources use spread = 0 and take confidence from their calc script, exactly like a CEX feed.