Price feed reads

The file and URL for this page still read internal-oracle, a legacy slug. The page is the consumer-side reference for the external feed; INTERNAL mode is the par helper below.

Every AIMM leg prices off a lane of the chain’s mark store, named by its OracleConfig. This page covers the consumer side: the read shape PoolFactory.getFeed returns, the state each push overwrites, the bounds a push must clear, and the wire format of the signed push API. The one exception is the internal par helper, which returns a fixed mark of 1.0 for cash-collateralized 1:1 tokens, where a fixed 1.0 mark is correct and a live feed would only add a manipulable input. Security properties and the mode reference: Mark Relayer.


1. Oracle modes

OracleConfig.mode picks one of two:

  • EXTERNAL: the mark is the primary (P) tier of the leg’s lane (OracleConfig.lane, below 64) in the chain’s one mark store; every spoke also bands against the same lane’s reference (R) tier.
  • INTERNAL: the par helper above, legal only on cash-collateral 1:1 spokes and never on the base.

The mode reference is Mark Relayer; per-pool selection is Curation §3.2.


2. Unified trust schema

The off-chain mark pipeline attests levels (mark, σ, confidence) and signs them; the chain stores them directly and bounds each push by a volatility-adaptive deviation band. No on-chain EMA smoothing: that lives at the source.

FieldOff-chain attestsChain storesSwap consumes
PriceFresh mark (30s close, θ + heartbeat)Raw mark; per-push move bounded by the adaptive bandRaw mark (mark1e18, 1e18 WAD)
VolatilityParkinson σ (48x30m bars)Signed σ direct; floored at realized |Δmark|/mark on every accepted pushsigmaPbps
UncertaintyMark CI (bps)Floored at realized |Δmark|/mark in bps; capped by MAX_CONFIDENCE_HALT_BPSRaw confidenceBps

Price authority is k-of-n distinct granted signers over one EIP-712 digest, enforced on-chain on the only push path (roster of at least 3, quorum at least 2, at most 16 signers); the submitting keeper must be a relayer on the tier’s committed list, and price authority comes from the signatures alone. The store lives in the Pool implementation’s own storage and runs MarkStoreP8 as its logic; pools read it by a 1-3 byte STATICCALL into Pool.fallback, one storage word per four lanes per tier. There is no oracle proxy and no oracle beacon: the store moves only with the Pool implementation, through the fleet upgrade (GOVERNANCE), which copies it across with migrateFrom. The single entry point is publishMarks(); there is no session variant. Roster installation and rotation: Mark Relayer.


3. FeedData (read shape)

struct FeedData { uint256 mark1e18; // fresh mark, 1e18 WAD. QUOTE SOURCE. (storage encoding is version-specific; see Oracles §2.) uint32 sigmaPbps; // stored σ (PBPS): signed sample floored at |Δmark|/mark. PRICING INPUT. uint32 updatedAtSecs; // the lane's observation second: the source second, see below. uint16 ttlSecs; // freshness window (s). uint16 confidenceBps; // mark 1σ CI (bps), decoupled from σ. uint16 flags; // feed flags. bit0 = FEED_HALT_BIT (guardian fast-freeze, fail-closed). uint16 maxDevBps; // class band FLOOR (bps), not its max: band ceiling = 10x this. uint48 sourceTsMs; // updatedAtSecs in ms. }

PoolFactory.getFeed(feedId) (primary) and feedOf(feedId, t) return this shape for the lane setFeedLane maps; the mark is a plain 1e18 WAD, 0 on a dark or halted lane. sigmaPbps despite its name is not an on-chain EMA: it is the signed σ stored directly, floored at the realized \|Δmark\|/mark on every accepted push (a compromised-signer backstop, §4).

Two P8 specifics:

  • updatedAtSecs and sourceTsMs are the same instant: the store keeps one observation second per lane (the word’s clock minus the lane’s 12-bit age) and no separate landing time.
  • Four lanes share a storage word and its clock, but each keeps its own age, so one lane’s staleness never leaks onto its word-mates. An age that saturates at 4,095 s reads stale under every class.

A push emits no event. Push cadence and landing latency come from the push transactions: the calldata carries each tier’s source second and the lanes it carried, the receipt its block. Which lanes advanced, skipped or went dark is read from the lane words at that block (a state diff).


4. Stored state (no on-chain recurrences)

No price EMA, no σ-EMA fold. Each accepted push overwrites the mark (mark1e18) directly, so stored state is what was signed. Smoothing is done at the source, which has the full tape.

  • σ floor (compromised-signer backstop): on every accepted price entry the stored σ is max(signed σ, |Δmark|/mark in PBPS), and the stored confidence is floored the same way at the move in bps, regardless of whether the segment carried a σ byte for that lane. Every σ read is clamped to the lane class’s minSigma. A signer cannot sign σ = 0 to make a mark-then-self-swap round trip spread-free, and a price-only blob cannot leave the stored σ below the move it just made.
  • σ without an advance: a σ byte on a lane whose mark did not advance writes only on a dark, anchored lane, and only to raise the σ it heals against; a replayed segment can never walk a dark lane’s σ back down.
  • Per-push deviation band: each segment must satisfy |Δmark|/mark <= maxDev + min(6·(σ_pbps/100)·√(dtSource/1800), 9·maxDev) in bps, volatility-adaptive off the stored prior σ, against the live mark or a dark lane’s anchor. A breach darkens that lane rather than reverting the push: the lane keeps its mark as the anchor and reads markless, visible only in its lane word. A lane with no mark (just registered, or reanchored by the owner) has nothing to band against and its first push is unbanded. Full derivation: Mark Relayer.
  • maxDev is the band floor, not its maximum: the ceiling is exactly 10 × maxDevBps, so at a stable floor of 50 bps a single segment may move a stable’s mark by up to 500 bps, a fifth of the widest base band (2,500 bps). Size the floor against that ceiling.

5. Push API

publishMarks() on the Pool implementation is the only mark path (wire 8). Its calldata is segments back to back, each one tier at one source second, each carrying its own quorum:

segment = t:u8 (1 primary, 2 reference) | srcSecs:u32 | laneMask:u64 | per set lane, ascending: markF:u32 | code:u8 [| σ8:u8 when code & 0x80] | nSigners:u8 | signers:20B* | k:u8 | nRelayers:u8 | relayers:20B* | k × (r‖s‖v)
  • srcSecs is the absolute source second, at most 5 s ahead of the chain and at most 120 s old.
  • markF is a 25-bit mantissa with a self-describing exponent; there is no per-feed bias.
  • code & 0x7F is the confidence code (0..61); σ8 is σ in an 8-bit float, never read below the class floor.
  • The roster prefix must hash to the tier’s commitment; msg.sender must be one of its relayers; the k signatures recover to strictly ascending roster members.

The digest is EIP-712 BatchQuoteV4(bytes32 blobHash) over the segment up to the quorum tail, domain name "BTR ExternalOracleV4", version "2", per-tier verifyingContract (tierVerifier).

function publishMarks() external; // segments in calldata function register(uint8 lane) external; // owner, LISTING function setAuth(uint8 t, address[] calldata signers, uint8 k, address[] calldata relayers) external; // owner, GOVERNANCE function halt(uint8 lane, uint8 tierMask) external; // guardian, steward or owner, instant function unhalt(uint8 lane, uint8 tierMask) external; // owner only function reanchor(uint8 lane, uint8 tierMask) external; // owner, TUNING function cancel(bytes32 op) external; // guardian or owner: drop a queued op function classes() external view returns (uint256, uint256, uint256, uint256);

tierMask bit 0 is the primary tier, bit 1 the reference tier. halt freezes a lane for every pool at once: it reads markless and keeps its mark as the heal anchor; unhalt returns it dark, healing through the anchor band on the next push. reanchor drops the anchor of a dark lane so its next push lands unbanded, the release for a move past the 9 × maxDev cap. setAuth needs a signer roster of at least 3 (at most 16, strictly ascending), a quorum 2 <= k <= n and 1 to 16 relayers.

register lists a classed lane on both tiers, dark, markless and at its class σ floor: the first accepted push is unbanded. Lane bounds (ttl 1-4,089 s, maxDev 1-2,000 bps, minSigma) are the store’s immutable class table; retuning them is a new class table at a fleet upgrade.

Byte-exact lane and class decoding for the live generation: p8StoreWord, p8Conf, p8Sigma and decodeP8Classes in @btr-protocol/sdk (pool/storage.ts), and the store’s own golden vector (MarkStoreP8Golden.t.sol), matched byte for byte by the keeper encoder.


6. Consumer guide

NeedEXTERNALINTERNAL
Quotemark1e18constant 1.0 (MarkWordLib.peg)
Pricing σsigmaPbpsSTABLE_SIGMA_PBPS = 1000
CIconfidenceBpsgate feed only

PageCovers
Mark RelayerSecurity, k-of-n signed push path, storage layouts, whitelisting ceremony, signature-scheme rationale
Spread & Feesσ, confidence, staleness
Parametrizationθ, heartbeat, ttl
PoolThe consumer: how the mark enters a quote
Consuming Price FeedsReading getFeed from your own contract