Oracles and market hours
The order the adapter checks things in, the freshness rules, the calendar arithmetic, and what a weekend does to a position.
The narrative is on oracles and market hours. This page is the decision procedure and the clock arithmetic behind them.
Adapter status
Each price feed sits behind a ChainlinkFeedAdapter. Its status() checks, in this order, and
returns at the first match (ChainlinkFeedAdapter):
| # | Condition | Status |
|---|---|---|
| 1 | The Stock Token's issuer has paused the oracle (oraclePaused()) | OraclePaused (4) |
| 2 | Answer , or updatedAt | InvalidAnswer (5) |
| 3 | HardStale (3) | |
| 4 | The calendar says the market is closed now | MarketClosed (1) |
| 5 | Within the reopen grace | MarketClosed (1) |
| 6 | Fresh (0) | |
| 7 | Otherwise | StaleInSession (2) |
Where the age of the answer is
(ChainlinkFeedAdapter), and the reopen grace holds when
— that is, the market was closed at some instant since the answer was published, within the last
in-session window, so the answer is a price from before the closure and the feed has not yet
published in the new session (ChainlinkFeedAdapter). The interval
is checked as a whole: against each holiday range, and against the weekly window's start offset
(as configured and one hour earlier, for daylight saving)
(ChainlinkFeedAdapter). So a closure shorter than the window, such
as a one-day holiday, counts as fully as a weekend.
Three consequences of the order:
- The hard cap is checked before the calendar and is never softened. A feed that has stopped is
unusable at
maxAgeHard, weekend or not. - The calendar is checked before the age. A price that is one minute old is still
MarketClosedwhile the market is shut. - The grace is checked before the age, too. An answer published before the last closure is
MarketClosedduring the grace even when it is younger thanmaxAgeInSession— after a one-day holiday, the pre-holiday answer is notFresh. A round published after the reopen starts the interval after the closure, so it isFreshimmediately. The grace ends on its own: once a new round lands, or once the clock ismaxAgeInSessioninto the session.
WETH and USDG adapters have no calendar, so for them steps 4 and 5 never apply.
What serves a price, and what needs Fresh
Two different questions, answered by two different functions.
Can a price be read? latestRoundData on the adapter reverts for OraclePaused,
InvalidAnswer, HardStale and StaleInSession, and serves the last answer for Fresh and
MarketClosed (ChainlinkFeedAdapter). The TokenDataStream in front
of it adds its own age cap and a positivity check
(TokenDataStream · 0xCa6c…3045). So over a weekend, health checks and valuations
keep working on Friday's price.
Is the price Fresh? Actions that open or raise risk require priceStatus to be exactly
Fresh (0) for both the collateral and the borrow token
(LendingPoolRouter):
| Action | Needs Fresh on both tokens | Source |
|---|---|---|
| Borrow | yes, when requireFreshForBorrow | LendingPoolRouter |
| Liquidate | yes, when requireFreshForLiquidation | LendingPoolRouter |
| Withdraw collateral, with debt | yes, when requireFreshForBorrow | LendingPoolRouter |
| Withdraw collateral, no debt | no | |
| Supply collateral, repay | no — reads no price | |
| Earn vault deposit, withdraw | no — reads no price |
Both flags are on for every market (read 12 October 2026, again at block 92690105, on all five markets at block 94915008, on MSFT/USDG and AAPL/USDG at block 99393260, and on QQQ/USDG, GOOGL/USDG and AMD/USDG at block 113204091). A stale USDG feed therefore stops borrowing and liquidation in every market at once.
Freshness windows
| Mainnet (block 86868761) | |
|---|---|
maxAgeInSession, all four launch adapters | 93,600 s (26 h) |
maxAgeHard, all four launch adapters | 349,200 s (97 h) |
TokenDataStream defaultMaxPriceAge | 349,200 s (97 h) |
Read on 12 October 2026, and unchanged at block 92690105. The SPCX and TSLA adapters, added on 22 October 2026, have the same two windows, and TokenDataStream's effective cap for both tokens is 349,200 s (block 94915008). So do the MSFT and AAPL adapters, and the QQQ, GOOGL and AMD adapters added on 13 November 2026, with the same 349,200 s cap for their tokens (block 113204091). The TokenDataStream cap is a backstop, set equal to the adapters' hard cap so the adapter decides.
Every feed has a 24-hour heartbeat, so 26 hours in session leaves two hours of slack between rounds.
A weekend is longer than 26 hours, which is why the calendar matters: from Friday's close to the
Sunday reopen the status is MarketClosed, not StaleInSession. After the reopen, Friday's price
is older than 26 hours, so it is not Fresh; the reopen grace keeps it MarketClosed rather than
StaleInSession until the first new round makes it Fresh. Borrowing and liquidation wait for that
round. After a one-day holiday the pre-holiday price can still be under 26 hours old at the
reopen; the grace keeps it MarketClosed all the same, and the first new round — about a minute
after the reopen, as measured after Labor Day 2026 — makes it Fresh. The hard cap, 97 hours, has to outlast the longest closure,
a weekend joined to a holiday (about 81 hours measured).
The calendar
Whether the market is open is computed on-chain by MarketCalendar
(MarketCalendar · 0x9181…aD04).
Position in the week. Seconds since Monday 00:00 UTC:
The Unix epoch fell on a Thursday; adding three days moves the origin to Monday.
The weekly window is stored in those units and may wrap past Monday 00:00. It is closed when
Daylight saving. With autoDst on, both ends move one hour earlier while US daylight saving is
in effect: from the second Sunday of March at 07:00 UTC to the first Sunday of November at 06:00
UTC, computed on-chain from the date.
Holidays are absolute ranges ; the market is closed inside any of them.
Mainnet: (Saturday 01:00 UTC), (Monday 01:00 UTC) — Friday 20:00 to
Sunday 20:00 Eastern Standard Time. autoDst is on and 21 holiday closures are configured
(closureCount() at block 92690105).
Worked: at (Monday 19 October 2026, 14:36:43 UTC, block 92690105):
Daylight saving is in effect, so the window is : Saturday 00:00 UTC to Monday 00:00 UTC,
still Friday 20:00 to Sunday 20:00 Eastern. It wraps (), and
is false, is false: open. Five days later,
: closed. The calendar agrees: at that block weeklyCloseWindowAt returned
, and isOpen returned true for and false for .
A weekend
Scroll sideways to see Monday →
What a weekend does to a position
The price is frozen; the debt is not. Interest keeps accruing every second the market is closed, and is booked at the next accrual. Over a closed window of seconds, at an unchanged rate, a position's debt grows by
and its health factor falls by the same proportion, on a price nobody can react to.
Worked, the illustrative position from position health over the 48-hour weekly window at the illustrative market's 2.3759375% APR:
HF moves from 1.22533 to 1.22517 — negligible at that rate. At 90% utilisation on the same launch curve (54% APR) the same weekend adds about 7.11 USDG and HF falls to 1.22171. The real weekend risk is the price gap at the reopen, not the interest; see risks.
Earn vault math
Total assets with pending interest, share conversions and their rounding, withdrawal limits, allocation, the high-water-mark fee, and the net rate.
Parameters
Every parameter and total the Math pages use, per market, as read on mainnet with the block each was read at, and the illustrative inputs of the worked examples.