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;INTERNALmode 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.
| Field | Off-chain attests | Chain stores | Swap consumes |
|---|---|---|---|
| Price | Fresh mark (30s close, θ + heartbeat) | Raw mark; per-push move bounded by the adaptive band | Raw mark (mark1e18, 1e18 WAD) |
| Volatility | Parkinson σ (48x30m bars) | Signed σ direct; floored at realized |Δmark|/mark on every accepted push | sigmaPbps |
| Uncertainty | Mark CI (bps) | Floored at realized |Δmark|/mark in bps; capped by MAX_CONFIDENCE_HALT_BPS | Raw 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:
updatedAtSecsandsourceTsMsare 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’sminSigma. 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. maxDevis the band floor, not its maximum: the ceiling is exactly10 × 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)srcSecsis the absolute source second, at most 5 s ahead of the chain and at most 120 s old.markFis a 25-bit mantissa with a self-describing exponent; there is no per-feed bias.code & 0x7Fis the confidence code (0..61);σ8is σ in an 8-bit float, never read below the class floor.- The roster prefix must hash to the tier’s commitment;
msg.sendermust be one of its relayers; the 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
| Need | EXTERNAL | INTERNAL |
|---|---|---|
| Quote | mark1e18 | constant 1.0 (MarkWordLib.peg) |
| Pricing σ | sigmaPbps | STABLE_SIGMA_PBPS = 1000 |
| CI | confidenceBps | gate feed only |
7. Related
| Page | Covers |
|---|---|
| Mark Relayer | Security, k-of-n signed push path, storage layouts, whitelisting ceremony, signature-scheme rationale |
| Spread & Fees | σ, confidence, staleness |
| Parametrization | θ, heartbeat, ttl |
| Pool | The consumer: how the mark enters a quote |
| Consuming Price Feeds | Reading getFeed from your own contract |