Chapter 06 · Quotes and the oracle
Quotes and the oracle
USDC and cirBTC, their Chainlink feeds, staleness, Circle pauses and quoteHalted.
A quote is the currency a pool is priced in and pays fees in: USDC or cirBTC. The QuoteRegistry decides which quotes new launches may use, what their decimals are and where their USD price comes from. It is read at launch time for the opening price and, for pools with Halt Guard, on every buy.
The registry
struct Quote { address feed; uint48 heartbeat; bool enabled; uint8 decimals; }
mapping(Currency => Quote) public quotes;
The owner adds a quote with add(quote, feed, heartbeat). The call checks that the feed has code and reports 8 decimals, reads the ERC-20's decimals() (a native quote counts as 18) and accepts 6 to 18, storing the value so the factory and the app never have to ask the token again (quoteDecimals(quote)). A quote can later be disabled for new launches with disableForNewLaunches, but it can never be removed and its feed can never be swapped: the registry is append-only. Live pools do not depend on the registry after launch, except through the Halt Guard read, which never reverts for a known quote.
| Registered on Arc | Address | Decimals | Chainlink feed |
|---|---|---|---|
| USDC (ERC-20 view of native USDC) | 0x3600000000000000000000000000000000000000 | 6 | USDC / USD 0x84EA90AC252Dc437031461836DB5164219147905 |
| cirBTC | 0x171A4217b86A807A64eB94757Db6849fb4bDbAA0 | 8 | BTC / USD 0xa109B535C70C8Be9995be64Bb6751AcDB27e03De |
| Heartbeat | 90,000 s (both feeds publish at least every 86,400 s and answer in 8 decimals) |
Native USDC (address(0), 18 decimals) is deliberately not registered: the ERC-20 view is the same balance with a 10¹² factor and is what every other Arc venue quotes in, and keeping trades on the transferFrom path avoids Arc's native-value rules (see Security).
Launch-time price: priceUsd8
priceUsd8(quote) is strict. It reverts unless the quote is known and enabled, the sequencer feed (if configured) reports up and has been up for more than the one-hour grace, the answer is positive, updatedAt is non-zero and not in the future, and the answer is younger than the quote's heartbeat. The factory calls it once per launch; a revert fails the launch with the registry's error.
Live-pool read: quoteHalted
quoteHalted(quote, maxStaleness) is lenient by design: it never reverts for a known quote, because a reverting read would brick a pool. It returns true when buys should stop.
The token flags are read with staticcall and a revert or a short return counts as false, so a plain ERC-20 quote passes that step. Circle's USDC and cirBTC contracts expose paused(); oraclePaused() is also probed for compatibility and simply reads as false on Arc's tokens.
No sequencer feed
Arc is a Layer 1, not a rollup, so there is no L2 sequencer uptime feed to consult. sequencerFeed is unset and sequencerUp() returns true. The owner-only setSequencerFeed remains in the contract for completeness; it is not expected to be used on Arc.
What the app shows
Every token page has a Quote status card: the feed's USD price and age, the max age the pool enforces (its Halt Guard setting, else the registry heartbeat) and the registry's own quoteHalted read. A Halt Guard pool whose quote is halted shows "buys halted" above the swap box and the Buy button is disabled with the reason before a transaction can revert; sells stay open. Discover marks such pools with a "buys halted" pill and other pools whose feed is stale with "quote halted" (informational: without Halt Guard the oracle never blocks a trade). Every quote-denominated figure in the app carries a secondary "≈ $" value from the same feeds; the quote-native number stays primary.
What can make Halt Guard trip
- Feed goes stale (no update for longer than
haltMaxStaleness): Halt Guard pools refuse buys until the next update. Pools without Halt Guard keep trading on the last AMM price; the oracle is not used by the AMM at all. - Bad answer (zero, negative,
updatedAtof zero or in the future): treated like a stale feed. - Circle pauses the token: transfers of USDC or cirBTC fail regardless of Hookarc. Halt Guard additionally refuses buys so a trader does not pay quote into a pool whose quote cannot move.
- Quote disabled in the registry: new launches with that quote fail; existing pools are unaffected and Halt Guard ignores the flag.
contracts/src/QuoteRegistry.soladd(), quoteDecimals(), priceUsd8(), quoteHalted(), sequencerUp()contracts/script/ArcAddresses.solUSDC, cirBTC and their feedscontracts/test/QuoteRegistry.t.solevery revert path of priceUsd8 and the decimals checkcontracts/test/HaltGuard.t.solstale, bad-answer and paused behaviour