Mark Relayer

One page for everything the mark-pushing role touches: the relay key’s authority, where prices come from, the on-chain k-of-n quorum that is the consensus, the roster ceremony, the per-lane guard chain, the operator runbook for btr-keeper oracle, and the store and wire reference the role depends on.

SectionCovers
1. The roleWhat the relay key can and cannot do; where marks come from
2. Threat modelOracle-manipulation classes and what resists them
3. QuorumThe on-chain signature check, the digest, the push wire, access control, scheme rationale
4. The roster ceremonyRoster commitment, rotation, reference disjointness
5. Push guards, staleness and bandsThe guard chain, per-push vs cumulative bounds, TTL, deviation bounds, LVR and OEV
6. Keeper operationsTriggers, startup gates, reverts, liveness guard, what to watch, escalation
7. Store referenceWhere the marks live, lane classes, FeedData, consumer reads
8. Oracle generationsWhat changed generation to generation, in one table
9. Transparency: verify it yourselfRe-verifying the whole chain client-side
10. RelatedNeighbouring pages

1. The role

For the operator running btr-keeper oracle, the process relaying signed marks into the chain’s mark store (MarkStoreP8, run by the Pool implementation). Metrics: Observability.

1.1. What the key is, and what it is not

The push path, publishMarks() on the Pool implementation, accepts a segment only from a relayer listed in that tier’s roster commitment. Price authority is the k-of-n signature set recovered against the committed signer roster; msg.sender pays gas. There is no session path: every segment is fully quorum-signed. The relay key authors nothing; it only chooses when to submit an already-signed segment. It must not be the deployer, the owner, a guardian, or an attester.

The contract does not enforce that separation. The relayer list decides which addresses may submit, not what else they hold. Role disjointness of the relay address is verified at the arming ceremony, against the live owner, guardian and roster.

The relay key canThe relay key cannot
Land a validly co-signed segment, paying gasAuthor a mark. It holds no attester key and the store verifies signatures, not the sender beyond the list
Choose when to push and which lanes ride alongLand a lane outside the deviation band, or a source second a lane has already accepted. Both fail per lane, not the whole segment
Stop pushing, which halts nothing but ages every lane toward its TTLHalt, reanchor, change a class or un-halt anything. Guardian, steward and owner levers (Guardian)

Losing the relay key is a liveness incident only. The other relayers on the list keep pushing; replacing the key is a roster rotation (setAuth, GOVERNANCE). The cutoff for a compromised attester key is the same rotation, and until it lands the guardian halts the affected lanes. A compromised attester quorum is bounded by the reference tier and the guardian, not by the relay.

1.2. Where prices come from

BTR does not acquire or aggregate prices. It consumes attested marks from high-frequency off-chain mark providers and re-verifies them on chain. A provider must supply a mark, a volatility estimate, a confidence interval and a source timestamp, land inside the lane’s TTL and survive the per-push deviation band. Which providers serve which lane is a deployment fact: deployed instances.

The signing quorum lives off chain. Each signer holds its own key on its own node, independent of the others. The replica serving a quote builds the segment from its own aggregated view, signs it, and proposes it to its peers; a peer countersigns only after re-validating every record against its own independent market view (price tolerance, timestamp skew and age, volatility/confidence understatement). Below quorum, no quote is served. A signed segment reaching the chain therefore means k independent processes, holding k independent keys, each independently agreed this price was correct at this time.

The aggregation method, the signing service, the quote endpoint and key management are off-chain provider internals, not reproduced here. What follows is on-chain BTR only.


2. Threat model

2.1. Oracle-manipulation classes

Failure modes that have repeatedly drained oracle-priced venues:

VectorMechanism
Single-key compromiseOne stolen producer key pushes an arbitrary mark, the pool prices off it, an attacker swaps the drained side.
Gradual walkNo single push looks unreasonable; a compromised producer walks the mark in many small steps until collateral is gone. A per-push sanity band leaves the sum unbounded.
Future-dated reportA far-future timestamp clears the replay guard once, then freezes the lane permanently at a stale price to be picked off.
Volatility understatementA producer signs σ=0 to collapse the pricing spread, then round-trips against a spread-free mark.
Source manipulationNo key stolen, no producer misbehaving: an attacker moves a majority of the venues the composite is built from, and the quorum faithfully signs an aggregate of manipulated inputs. Nothing downstream can tell it from a real move. The guards below bound how fast that mark walks the pool, never whether it is right; the defenses are off-chain and economic (Security Overview).

Design goal: track a real crash faithfully, never brick, and never let one or few compromised keys one-shot or slow-walk the mark.

2.2. Manipulation resistance

  1. External-mark quoting: the quote source is a mark aggregated off-venue, not pool-internal reserve state, so a single-block, flash-loan or sandwich move of the reserves cannot move the quote.
  2. Volatility-adaptive per-lane band: a segment moves a lane at most 10dmax, so a single compromised segment cannot one-shot the mark; a breach darkens the lane, it does not revert (§5.2).
  3. Roster governance: every segment needs k signatures from the tier’s committed roster (n≥3, 2≤k≤n). The roster, k and the relayer list change together, only through setAuth at the GOVERNANCE tier.
  4. Guardian fast-freeze: guardian, risk steward or owner can halt(lane, tierMask) on either tier, instantly, for every pool at once; only the owner can unhalt. A halted lane reads markless and fails closed in FeedMathLib.gate; release semantics and the reanchor past the ceiling: §5.2.

3. Quorum

3.1. The on-chain quorum is the consensus

There is no separate consensus protocol between BTR and the mark provider. The consensus is the on-chain signature check. The store holds one 128-bit roster commitment per tier: the high half of keccak256(nSigners | signers | k | nRelayers | relayers), signers strictly ascending, addresses packed. The push carries the roster in calldata; the store hashes it and compares it to the commitment, so a roster costs one storage word however many signers it holds.

Authorization, per segment (MarkStoreBase._quorum):

  1. The roster prefix of the tail must hash to the tier’s commitment, and msg.sender must be one of its relayers.
  2. The tail carries k 65-byte signatures over one EIP-712 digest. Each is recovered against the same digest, and every recovered address must be strictly greater than the previous one and present in the roster. Strict increase is the deduplication check: a repeated signature fails on its second appearance, an unsorted set at the first inversion. k accepted signatures prove k distinct roster keys. A malformed tail or k=0 reverts InvalidBlobHeader; anything else reverts NotAuthorized.
  3. The stride is a fixed 65 bytes with no EIP-2098 compact form. The signature count is the quorum claim, so its encoding must be unambiguous.
  4. A segment’s lanes are written only when its quorum passes: a failed quorum reverts the whole push.

A signature binds the price, not the order segments land in: an open push would let anyone holding two signed segments push the extreme one, trade, push the other and trade back. Hence the relayer list. One stolen signer key can push nothing on its own.

3.2. What the digest binds

The digest is EIP-712 BatchQuoteV4(bytes32 blobHash) over the segment’s own calldata (header and lane entries, not the quorum tail). The domain is name "BTR ExternalOracleV4", version "2" (the P8 layout’s; a segment of another wire never verifies here), chainId, and a per-tier verifyingContract: the PoolFactory for the primary tier, address(keccak256(factory, 2)) for the reference tier (tierVerifier). The name is a frozen signing label (MarkStoreBase); the V4 oracle contract it names is retired.

FieldWhat it prevents
chainIdCross-chain and post-fork replay.
verifyingContractCross-tier replay. A segment signed for the primary tier cannot verify on the reference tier, or the reverse, even when the two share signers. Without it the reference band (§5.2) would fall to message reuse.
versionCross-layout replay.

3.3. The push path: wire, quorum, and per-lane guards

publishMarks() reads its calldata as segments back to back; each is one tier at one source second.

Wire. Header 13 bytes: t:u8 | srcSecs:u32 | laneMask:u64. Then, per set lane in ascending order, markF:u32 | code:u8, and a σ8:u8 when code & 0x80 is set; code & 0x7F is the confidence code (0..61). Then the quorum tail. markF is a 25-bit mantissa with a self-describing exponent; there is no per-feed bias and no epoch.

Whole-push reverts:

ErrorCause
InvalidBlobHeader()t not 1 or 2, empty lane mask, truncated segment, a confidence code above 61, or a malformed tail
FutureTimestamp()srcSecs leads block.timestamp by more than 5 s
StaleData(age, 120)srcSecs trails block.timestamp by more than 120 s
NotAuthorized()Roster does not match the commitment, sender not a listed relayer, or a signature not from a strictly ascending roster member

Per-lane, fail-soft (the rest of the segment lands):

OutcomeCause
SkippedLane unregistered, or halted on this tier
SkippedsrcSecs not past the lane’s own observation second (replay guard)
DarkenedDeviation band breach (§5.4): the lane keeps its mark as anchor and reads markless
AcceptedWithin the band, or the lane had no mark (first push, or after reanchor)

The push emits no event. Signed segments are the transaction’s own calldata and every lane is a storage read, so cadence comes from the calldata and the receipt, and accepted, skipped and darkened lanes from the lane words at that block (§9). Each group of four lanes is one storage word per tier, written once per segment: a 9-lane primary push costs 73,972 gas full-transaction.

3.4. Access control

RoleCapabilities
OwnersetAuth(t, signers, k, relayers) at GOVERNANCE; register(lane) at LISTING; reanchor(lane, tierMask) at TUNING; unhalt(lane, tierMask), instant
Guardian, risk steward or ownerhalt(lane, tierMask), instant
Guardian or ownercancel(op) on any queued store op
PoolFactorymigrateFrom(oldImpl) during a fleet upgrade, once, into an empty store: P8 source only, copied raw. Queued store ops do not carry over; re-queue a pending setAuth after the upgrade
SignerOne of the k-of-n roster keys; authorizes a segment by signature. Not a caller role

Timelocked ops queue on first call and run on a repeat of the same calldata inside the 7-day grace window. Before the AccessControl quorum policy is armed (the ceremony), they run at once. Every store op checks the store is the one pools read (_live: the factory’s implementation is this one), so nothing lands on, or queues against, a retired or pending implementation.

There is no instant signer revoke. A leaked key is answered by halting the lanes it could move, then rotating the roster at GOVERNANCE; with k≥2, one leaked key moves nothing by itself.

3.5. Signature scheme rationale (k ECDSA, not aggregation)

The quorum is k concatenated 65-byte ECDSA signatures over one shared digest, not a BLS or Schnorr aggregate:

  • Aggregation is a gas regression on EVM chains. BLS verification needs a BN254 pairing; Schnorr-style schemes need per-signer public keys on chain plus an off-chain nonce round. At a roster of at most 16, k plain ecrecover calls are cheaper and simpler.
  • Membership changes stay cheap. A rotation is one commitment word. A fixed-committee aggregate would turn it into a key re-share.
  • No interactive nonce round. Each replica signs the shared digest; the relayer sorts and concatenates.
  • The quorum amortizes per segment. All k signatures cover one digest, so a segment of N lanes pays verification once.

4. The roster ceremony

Loosening price authority (new signers, lower k, new relayers) and hardening it run the same path: setAuth at GOVERNANCE, guardian-or-owner cancellable. The instant lever is the lane halt, not a roster edit.

4.1. Genesis: no 1-of-1 bootstrap

setAuth refuses fewer than 3 signers, k<2 or k>n, an unsorted or zero signer, and an empty or oversized (> 16) relayer list. A deployment never passes through a single-key state. The ceremony script (Protocol.s.sol) commits both tiers’ rosters, reads them back, and commits the same relayer set to both tiers. Each keeper tier config lists exactly the manifest’s relayers for that tier, and a keeper test fails if the two drift, so the addresses that push are the addresses committed.

4.2. Rotation

setAuth(t, signers, k, relayers) replaces a tier’s whole commitment. Off-chain, a new signer is a standalone key on its own node, not derived from an existing signer. The mark provider’s signer catalog and the committed roster must change together: a roster the provider does not sign for stops that tier.

4.3. Ceremony parameters

ConstantValueMeaning
MAX_ROSTER16Cap on signers and on relayers per tier
min signers / min k3 / 2No 1-of-1 or 2-of-2 roster
Roster delayGOVERNANCE tier of AccessControl.GOV_DELAYS() (schedule)Delay on every setAuth
Grace7 days (ConstantsLib.GRACE_PERIOD_SECS)Execution window after maturity

4.4. Independent reference: signer disjointness

The cumulative bound (§5.2) works only if the reference tier is operationally independent of the primary. Both tiers live in one store under one AccessControl owner; what can differ is the roster.

Signer disjointness is not enforced on chain. Nothing compares the two tiers’ signers. Required configuration: disjoint rosters, signed by separate provider deployments. Per-deployment status: compare the two tiers’ AuthUpdated logs on the store.

Operating obligation. Every armed spoke band fails closed once its reference lane goes stale, so the reference tier is relayed on the same θ/heartbeat discipline as the primary, from its own signed segments (the per-tier domain means one tier’s segment never verifies on the other). Operator detail: §6.2.

Consequence. Where the rosters overlap by k keys, one compromise signs both tiers, the reference band walks in lockstep and never trips. Disjointness makes the second tier require a second, independent compromise.


5. Push guards, staleness and bands

5.1. How a push is guarded (defense in depth)

A verified quorum is necessary, not sufficient. Every lane passes a fail-closed guard chain before it writes a mark.

GuardBounds which vectorRule
k-of-n roster quorum (§3.1)Single-key compromisek distinct roster signatures per segment, or the push reverts
Monotonic srcSecs per laneReplay / reorderA lane’s own last-accepted source second must strictly advance; a non-advancing lane is skipped silently
Future-dated bound (5 s)Future-dated freezeReject a srcSecs leading the chain by more than 5 s
Absolute freshness (120 s)Withheld stale segmentReject a segment whose srcSecs is more than 120 s old; the read side gates on the class ttl
Volatility-adaptive bandPer-push spikeAt most 10dmax, normally a 6σ Brownian step over the source-time gap. A breach darkens the lane (§5.4)
σ and confidence floorUnderstatementEvery accepted mark floors the lane’s σ and confidence at the realized |Δp|/p
Class σ floor (minSigma)Understatement (baseline)Every σ read is clamped to the class floor, including a σ stored before the floor was raised
Reference band (refBandBps, lane R tier)Gradual walk (cumulative)Halt swaps once the primary diverges from the reference tier by the band (Flow Guards)
USD depeg bandNumeraire depegHalt the hub when the base’s USD mark leaves parity by its band (Flow Guards)
Guardian fast-freezeAny suspected incidenthalt(lane, tierMask), instant (Guardian)

5.2. Per-push band vs cumulative band

The signatures authorize authenticity, not magnitude. Two magnitude bounds run in series.

Per-segment (sanity). A segment moves a lane at most a 6σ Brownian step over the source-time gap, never more than 10dmax. The band binds per segment, not per transaction: segments stepping srcSecs by one second can chain about 125 advancing moves in one call. The quorum is trusted for magnitude; the cumulative bound is the reference tier.

A dark lane self-clears, up to the ceiling. The source-time gap grows while a lane is dark, so the band widens with it and a lane that fell behind during an outage clears once the true move fits. A lane whose age field saturates (4,095 s) reads stale under every class and bands at the 9dmax cap. Past 10dmax it does not clear.

Recovery past the ceiling is reanchor. reanchor(lane, tierMask), owner at TUNING, drops a dark lane’s anchor on the chosen tiers; its next push lands unbanded, as a first push does. Verify that push against the source before trading resumes. A class’s maxDev, ttl and minSigma are immutable in the store; retuning them is a new Pool implementation with a new class table, moved in by the fleet upgrade (migrateFrom keeps every registered lane and clamps stored σ to the new floor).

Cumulative bound. A per-segment band does not bound the total excursion. The reference tier closes that: every spoke leg arms refBandBps against its lane’s R mark, and swaps halt once the primary diverges past it. It is magnitude-based and push-rate-independent. Given disjoint rosters, draining a pool by walking the mark takes two simultaneously compromised quorums, and even then only within the reference band.

5.3. Staleness protection

  1. One fail-closed gate: FeedMathLib.gate reverts, in order, on HALTED, STALE, DEAD (mark 0) and UNCERTAIN, for the quote mark, the base mark and the reference mark alike.
  2. Freshness is measured from the lane’s own source second: a=t-tobs, a>τ⇒ StaleData(a,τ). τ is the lane class’s ttl (at most 4,089 s, so a saturated age always reads stale).
  3. Confidence halt: confidence > MAX_CONFIDENCE_HALT_BPS = 1,000 bps reverts ThresholdViolation. Exactly 1,000 bps still quotes. Below the line, confidence widens the spread (Spread & Fees §3.5).
  4. A mark landed this block is not gated. It has cleared the quorum, the source clock and the band. Rejecting it would hand every relayer a pool-wide outage switch. One pool call loads each leg’s mark word once at its start, so every leg of the call reads one mark.
  5. Staleness surcharge: the spread ramps with Zsσmax(0,a-g), grace g=min(τ/2,30s) (Spread & Fees §3.4).
  6. Depeg halt: the base reverts BaseDepegged when its USD mark leaves 1.0 by more than its band (Flow Guards).

5.4. Deviation bounds

Canonical statement of the per-lane band. Write p for the mark, dmax for the class maxDev in bps, σ for the lane’s σ (never below the class floor) in PBPS, Δt for the source-second gap since the lane’s own last accepted observation. A segment darkens the lane when δ>δmax:

δ=|pnew-pprev|·104pprev,δmax=dmax+min(6·σ100·Δt1800,9dmax)

The band compares against the live mark, or on a dark lane against its anchor, so re-entry is banded over the whole gap.

TermValueRationale
Z66σ sanity cap on a Brownian step
T1,800 sThe provider’s 30-minute σ window
X9Caps the band at 10dmax
dmaxclass field, 1 to 2,000 bpsMicrostructure floor, not the primary bound
Δtsource seconds since the lane’s last acceptSource time, not block time

σ and confidence are floored at the realized move on every accepted mark, whether or not the segment carried a σ byte. A σ byte on a lane that did not advance only raises a dark, anchored lane’s σ, sizing its own heal band without a price write.

Lane classes. Each lane has one of seven classes fixed in the store constructor: ttl 1 to 4,089 s, maxDev 1 to 2,000 bps, minSigma 65 to 4,063,232 PBPS. The ceremony builds the table from the chain manifest and bounds each field before packing.

  • σ ceiling: 4,063,232 PBPS, the σ8 code’s range.
  • Confidence: halts past 1,000 bps in FeedMathLib.gate.

5.5. Known risks: LVR and OEV

AIMM’s external-mark design changes which oracle risks matter. It does not make LP capital immune to adverse selection.

LVR (Loss Versus Rebalancing): push-latency form

Definition: LP loss vs a continuously rebalanced strategy at the true external price. Classical CFMM LVR comes from a reserve-implied mid that only moves when someone trades. AIMM quotes the lane’s pushed mark, so that channel is closed, but only that channel: the residual below is real and unrebated.

Residual risk: between the moment the true price crosses the keeper’s θ (or a heartbeat / CI-spike fires) and the correcting push confirming on-chain, informed flow can still trade against a frozen mark. Call this push-latency LVR. Pushes fire on three triggers:

  • A θ crossing.
  • Heartbeat expiry.
  • A CI spike: the feed’s confidence interval widening past a keeper-side threshold, which covers depeg and dispersion onset between θ crossings.

Primary controls:

ControlRole
Keeper theta_bps (θ)Bounds intended stale gap before a push
heartbeat_s ≤τ/2Liveness ceiling; ops hard-fail if violated
Pool minFeePbpsTrader pays ≈Spath/2; size so one-way cost covers typical stale gap
Provider freshness + poll_interval_msDetection latency before the keeper even builds a tx

Secondary / defense-in-depth:

ControlRoleCaveat
vegaBps → SvolWidens spread when σ is already highWeak on the first jump of a new regime
confidence → UconfTaxes uncertain marksHalt past MAX_CONFIDENCE_HALT_BPS
STALE_Z → Ustaleσa-g past the grace g=min(τ/2,30s)Zero while a≤g: does not price healthy intra-θ drift
Coverage skew (fixed protocol law)Taxes one-way inventory movesZero at coverage ≈1
kappaCovBps coverage wallConvex toll on coverage-declining outputRequired on every listed asset including the hub; a spoke never above its hub
minDispersionPbps / curveDepth near midTighter / more center-bumped = better UX, more size extractable per bp of gap
refBandBps / TTL haltCircuit breakersAvailability vs bleed

OEV (Oracle Extractable Value): update MEV at the push

Definition: MEV from ordering around the oracle update itself, not from slow between-push drift. Glossary: OEV.

On a chain with a public mempool, a keeper push is otherwise a publicly visible transaction before it lands; searchers can frontrun, backrun, or pre-position on a predictable heartbeat schedule. Private submission is a config requirement on the current wire: every push is routed directly to builder inboxes rather than the public mempool, and the keeper refuses to arm without that route configured. Which builders and payment addresses are wired is a deployment fact.

Primary controls:

ControlRole
minFeePbpsThe pool param that taxes both frontrun and backrun of a legitimate push
Private submissionRequired to arm; keeps every push off the public mempool
Atomic push + solver auction returning bid to LPsStronger capture (not shipped)

Important trade-offs:

  • Lower θ → smaller jumps per event, but more events → OEV frequency can rise even as LVR per gap falls.
  • heartbeat_s is a primary LVR control and a net OEV aggravant (pure wall-clock predictability).
  • The deviation band and the signer quorum defend compromised-key abuse, not third-party reordering around a legitimate push.
  • STALE_Z does not protect the immediate post-push backrun (a resets to 0).

Keeper θ and heartbeat are set per asset class, tighter θ and longer heartbeat for pegged assets, wider θ and shorter heartbeat for volatiles. They are off-chain keeper configuration, not on-chain parameters: the lane’s class exposes maxDev and τ, readable by anyone. Align pool minFee with the mean- or hard-gate policy chosen for each class.


6. Keeper operations

6.1. When the keeper pushes

Three triggers, evaluated per feed against the last landed on-chain push:

TriggerRule
Deviationthe live mark has drifted from the last landed mark past a share of the edge the pool is currently quoting for that leg
Heartbeatthe per-feed maximum time between on-chain pushes has elapsed
CI spikethe feed’s confidence interval has widened materially since the last push

The deviation trigger is edge-relative, not a static θ. The keeper mirrors the pool’s own spread computation per leg and pushes once the drift reaches a fixed share of it. A static threshold per feed would not track fee floors and risk parameters, which move independently of it. Confidence is excluded from the trigger basis, though the pool’s quote includes it: a freshness proxy in the push boundary would make a stale feed push less when it should push more.

CI spike covers the pinned mark: a depeg onset that holds the price flat while dispersion explodes trips neither the deviation trigger nor the heartbeat. A feed carrying no trigger of its own can ride along on an already-due blob when it is close enough to its own push boundary, so a batch lands one coherent view.

Heartbeat is a staleness bound the keeper enforces on itself, not a liveness watchdog. The ops rule coupling it to the contract is ttl ≈ 2·heartbeat, and the keeper hard-fails at startup on any feed whose heartbeat exceeds half its on-chain TTL.

There is no hard push-rate cap. Per-feed refractory gaps bound spam and per-feed hourly forecasts drive gas budgeting and alerting, but neither refuses a push: the binding ceiling is a daily gas-spend breaker, which degrades gracefully; a hard rate cap would mis-price the hour the mark moves most.

6.2. Both tiers, or neither

Every spoke bands against its lane’s reference tier and fail-closes when the reference goes stale, so refreshing only the primary bricks every spoke swap once the reference passes its TTL. The two tiers cannot share a segment: the per-tier EIP-712 domain means each needs its own signed segment. The keeper relays both from one store address and one relayer set; the feed-liveness guard in §6.5 watches both tiers. Each tier bands on its own σ: the reference keeper keeps the σ its segment carried rather than mirroring the primary’s.

The relayers of a tier run as one leader and standbys. A standby watches every lane of the segment it would relay, not one representative lane, and stands down once the leader’s push lands; a band-darkened lane therefore cannot hide the leader’s landing and trigger a duplicate relay. The keeper forwards exactly k signatures, since the store rejects a surplus, and ages the upstream quote on its floored source second, the same second the store checks.

6.3. Startup gates

The keeper refuses to arm rather than push into a misconfiguration: it aborts at startup on any config, chain, feed-roster or quorum mismatch, on an upstream catalog that does not cover every subscribed feed under the configured oracle address and chain id, and on a missing private-submission route (§5.5). Quorum is re-read at a bounded cadence through the run, not only at boot, so a roster rotation is enforced live. The anti-pick-off gate is evaluated over every supervised pool, checking each asset’s deployed minFeePbps against the push threshold actually in force, and the same invariant is independently enforced by the risk keeper (Risk Steward).

Live broadcasting requires two independent arming flags; neither is set by default, so the keeper’s normal state is to run the full loop and broadcast nothing, which is also how a config change is validated. The exact gate names and thresholds are operational and not published.

6.4. Revert taxonomy

Only header, source-time and quorum failures revert the whole push; everything price-shaped fails soft, per lane. There is no event and no accepted mask: read the lane words. A band-darkened lane pages from the keeper (a markless, unhalted lane after a successful receipt). The silent per-lane monotonic skip is covered by watching each lane’s observation age directly.

6.5. The feed-liveness guard

btr-keeper guards is a separate role with a separate key: it sweeps every lane on both tiers at the factory, pages a human, then halts what stays dead.

Its heartbeat is read from the pusher’s own configuration, so the heartbeat a guard checks is the heartbeat the pusher promises. Four gates bound a halt, each reporting distinctly: a rolling automated-action budget consumed only by a landed broadcast, the two arming flags, a guardian-bit read of AccessControl.perms(signer) at startup, and the broadcast itself.

Automated action is rate-limited; alerting is never bounded by that limit.

Load-time rules refuse a config that could go dark: at least one alert stage; the earliest alert fires strictly before the earliest halt; an armed load rejects zero addresses.

The halt leg signs as a guardian and inherits Guardian, including the owner-only unhalt: a guard-driven halt always ends with a human.

6.6. After a change

  • Liveness: the keeper marks a heartbeat after every successful tick; its probe restarts the process when that heartbeat goes stale.
  • Latency: p50/p99 per leg from the health surface (quote fetch, pre-submission, submit).
  • Triggers: one line per lane per relay decision, the effective θ and heartbeat, gas spend and the low-balance gate.
  • On chain: PoolFactory.feedOf(feedId, t).updatedAtSecs advancing on every lane, on both tiers.
  • Downstream: feeds.tradable_ratio and feeds.worst_age_ratio (Observability).

A newly registered lane is dark and markless: its first push lands unbanded. Get that push in under supervision and verify it against the source before the leg is listed. The same holds after a reanchor.

6.7. Escalate

SymptomEscalate to
An attester key believed leakedGuardian: halt the lanes at risk. Owner: setAuth rotation at GOVERNANCE
A lane pushing authentic marks you believe are wrongGuardian: halt(lane, tierMask)
Lane dark past 10·maxDev after an outageOwner: reanchor(lane, tierMask) at TUNING, then verify the first push. Plan an hour
A class’s ttl, maxDev or minSigma structurally wrongNew Pool implementation with a retuned class table, through the fleet upgrade at GOVERNANCE
Both tiers stale simultaneouslyIncident: every spoke is fail-closed. security@btr.markets

Nothing on this list is reachable from the relay key.


7. Store reference

To only read a BTR mark from your own protocol, go to Consuming Price Feeds. Data-flow view: Internal oracle.

7.1. Where the marks live

One mark store per chain, in the Pool implementation’s own storage. Pools read it by a 1-3 byte STATICCALL into Pool.fallback (one storage word per four lanes per tier); pushes and store governance DELEGATECALL the store logic MarkStoreP8. Each pool leg names a lane (OracleConfig.lane, below 64) and reads both tiers of it:

ModeValueQuote sourceWhen to use
EXTERNAL0the lane’s primary (P) markEvery priced leg, and always the base
INTERNAL11.0 peg; the P mark gates depeg onlyCash-collateralized 1:1 units only

Every spoke leg arms a reference band against the lane’s R mark; the base carries the USD depeg band instead (Flow Guards).

7.2. FeedData: the read shape

PoolFactory.getFeed(feedId) (primary) and feedOf(feedId, t) serve the IOracle.FeedData view of a lane, the feed id mapped by setFeedLane:

FieldRole
mark1e18Mark (1e18 WAD); 0 on a dark or halted lane
sigmaPbpsσ (PBPS), never below the class floor
updatedAtSecsThe lane’s accepted observation second
ttlSecsClass ttl τ
confidenceBpsMark CI (bps), decoded from the confidence code
flagsbit0 = halted
maxDevBpsClass maxDev
sourceTsMsupdatedAtSecs in ms

classes() on the store returns the lane-to-class map and the packed class table.

7.3. Consumer reads

NeedReadNotes
Spot / quotemark1e18The only quote source (§5.5)
Freshnessage vs τFrom the lane’s own observation second (§5.3)
UncertaintyconfidenceBpsWidens the spread; halts past 1,000 bps
Realized volsigmaPbpsVol band and staleness surcharge

8. Oracle generations

GenerationLayoutLanes / wordWireClockStatus
V164-bit, one feed per slot1v1shared per-push source timeRetired
V228-bit packed8v2per-slot decisecondsRetired
V322-bit packed, session grants10v4 diff wiredeciseconds since an epochRetired
V429-bit packed, cyclic clock8v5deciseconds since midnight UTCRetired
V5 (ExternalOracleV5)25-bit mantissa + 7-bit exponent, beacon proxy4v6per-lane source secondRetired before mainnet
P8 (MarkStoreP8)56-bit lanes, both tiers, class table4 per tier8per-word clock, 12-bit lane ageCurrent

Every generation keeps the FeedData read shape and the k-of-n trust anchor. In the current generation the store lives in the Pool implementation: there is no oracle proxy, no beacon and no separate oracle AccessControl. The store changes only with the Pool implementation, through PoolFactory.executeReferenceUpgrade, which calls migrateFrom on the new store before any pool moves.

Which generation a chain runs, and its live addresses: deployed instances.


9. Transparency: verify it yourself

No part of the trust chain depends on trusting a BTR server. The Oracle Transparency page in the BTR front-end re-verifies the model client-side:

  • Roster. Rebuilt from the store’s AuthUpdated logs per tier and checked against the on-chain commitment. A queued setAuth (OpQueued) is shown with its ETA, so a rotation is visible before it lands.
  • Lane table. Mark, σ, confidence, age, maxDev, halted status, per tier, from the lane words.
  • Push log. A push emits no event, so BTR keeps a record of every push the keepers land, one row per lane: trigger (deviation, heartbeat, confidence spike), source-to-chain latency by stage, distance from the reference price. “Verify” on a row fetches the signed payload, checks it against the transaction input from the chain’s own RPC, re-recovers the signatures in the browser under the tier’s domain (§3.2) and compares the roster they carry with the store’s on-chain commitment. The “k verified” badge is computed locally. The page’s own roster is read the same way: off the newest signed push, once the commitment matches.
  • Relayers. Listed as informational: “relayer, not price authority”.
  • Per-price redirect into the mark provider. Each decoded record links to the provider’s public verifier for the same (symbol, sourceTs) attestation.

PageContent
Internal oracleOracle modes and data-flow view
Spread & FeesHow σ, confidence and staleness affect fees
ParametrizationFeed configuration reference
Flow GuardsDepeg and reference-band circuit breakers
Security OverviewDefense in depth, threat model, trust assumptions
Access controlRoles matrix, timelock tiers, halt authority
GuardianThe levers this role escalates to
Risk StewardThe other bounded key, and the shared 2θ invariant
ObservabilityEvery metric named above, with healthy ranges
Consuming Price FeedsRead-side integration
Contract AddressesLive store and rosters per chain