Oracles
Chainlink feeds behind an adapter that knows when the market is shut.
Every price comes from a Chainlink Data Feed, read through a ChainlinkFeedAdapter that adds
one thing the raw feed does not have: an opinion about whether the market is open.
How many assets can have a market
Counted from Chainlink's reference data directory on 2026-09-17: 35 feeds carry US equity market hours, against roughly 194 Stock Tokens in existence. A market cannot exist without a feed, so that is the ceiling on which assets Paboxo can list — about 18% of them.
Every one of those 35 shares the same configuration: an 86,400-second heartbeat, a 0.5% deviation threshold, and 8 decimals.
You may see it stated that every Stock Token has a live price feed. The directory does not support that, and these docs follow the directory.
Why an adapter
A raw feed reports a price and a timestamp. For an asset that trades continuously, a stale timestamp means something is broken. For a Stock Token it usually means it is Saturday.
Treating those two identically produces one of two failures — freeze the protocol every weekend, or price positions on a number that has been frozen for 60 hours while pretending it is live. The adapter separates them.
Status
Every read returns a status, and the contracts act on it rather than on the raw price alone:
| Status | Meaning | |
|---|---|---|
| 0 | Fresh | Updated within the in-session window. Everything allowed |
| 1 | MarketClosed | The exchange is shut. Last price stands; borrowing and liquidations pause, withdrawals stay open |
| 2 | StaleInSession | The market should be open but the feed has not updated. Treated as a fault |
| 3 | HardStale | Past the hard cap. The price is not usable at all |
| 4 | OraclePaused | The feed's own issuer has paused it |
| 5 | InvalidAnswer | The feed returned something that cannot be a price |
MarketClosed is the only one of these that is a normal condition rather than a problem.
Freshness windows
Two limits per adapter:
- In-session max age — how stale a price may be while the market is open before it counts as
StaleInSession. - Hard max age — the absolute ceiling. Past it the price is
HardStaleand unusable, open or closed.
On testnet these are set to 30 days in-session and 60 days hard, read from the deployed adapters on 2026-09-17. Those are deliberately loose because the testnet feeds are mocks that nobody updates on a schedule. They are not the values a mainnet deployment should carry, and tightening them is an outstanding task rather than a decision already made.
oraclePaused
The feed's issuer can pause it. When that happens the adapter reports OraclePaused and the
protocol stops pricing on it, rather than continuing with the last number.
This is a real dependency worth naming: the party that can pause the feed is not the protocol and not you. See risks.
Market calendar
Whether the market is open is decided by a MarketCalendar contract, not by guessing from feed
timestamps. It holds:
- a weekly close window, stored as seconds since Monday 00:00 UTC — so it expresses "Saturday 01:00 UTC until Monday 01:00 UTC", not a time of day;
- automatic daylight saving, computed on-chain from the US rule, so the window shifts without anyone sending a transaction on the switch dates;
- a list of holiday closures as absolute ranges.
The NVDA and SPY adapters point at the calendar. The WETH and USDG adapters deliberately do not — those assets have no market hours.
Why not a TWAP or a pull oracle
A TWAP needs a liquid on-chain market in the asset to average over; Stock Tokens do not have one. A pull-based oracle would let whoever submits the price choose when to submit it, which for an asset with a scheduled close is an invitation rather than a risk. A push feed with a published schedule is the honest fit for an asset with published market hours.