09 / 14 · docs
Oracles
i only trust a fresh voice.
Chainlink feeds for tokenized stocks, why they freeze, and the staleness checks EVE runs.
simulation Simulation only. The protocol is not live. No transaction is ever sent.
EVE needs a price twice: at night, to size the advance, and at the open, to settle. Both come from Chainlink. The whole design rests on knowing when that price can be trusted.
What EVE reads#
Each stock has a Chainlink price feed on Base that reports its price in US dollars. A feed is a contract that stores rounds. Each round has two fields that matter here:
answer: the price;updatedAt: the time the round was written.
EVE never uses answer without looking at updatedAt.
| Stock | Feed address |
|---|---|
| NVDA | not decided todo |
| AAPL | not decided todo |
| META | not decided todo |
| GOOGL | not decided todo |
Why feeds freeze#
A stock's feed follows the real market for that stock. When the market is closed there are no trades to report, so the feed stops updating. It does not go to zero and it does not disappear: it keeps returning the last round, with its old timestamp.
That gives a feed three states over a week:
| State | When | What the feed returns |
|---|---|---|
| Live | During the session | Recent rounds, updated as the price moves. |
| Frozen at the close | Nights, weekends, holidays | The last round of the session: the closing price, with a timestamp that gets older every minute. |
| Frozen for another reason | Trading halt, corporate action, outage | The last round before the event. |
A contract that reads only answer cannot tell these apart. A contract that reads updatedAt can.
At night: the reference price#
After the close, the frozen value is exactly what EVE wants: the official closing price. It is the reference price, and it is used for one thing only, sizing the advance.
advance = quantity × reference price × advance rate
It is never used to settle. That is why a frozen feed is harmless at night.
At the open: fresh rounds only#
i only trust a fresh voice.
At the open the frozen value becomes dangerous: for a moment after 9:30 AM, the feed still shows yesterday's close. Settling on it would pay sellers the old price and leave the pool holding the overnight move.
So a round counts as fresh only if both conditions hold:
- it was written at or after the opening bell;
- it is younger than the staleness limit, 120 seconds todo.
function isRoundFresh(round, openAt, now, maxAgeSeconds) {
return (
round.updatedAt >= openAt && // written after the bell
now - round.updatedAt <= maxAgeSeconds * 1000 && // and not stale
round.price > 0
);
}
The first condition uses the opening time from the market calendar, including early closes and holidays. See Market hours.
The averaging window#
The settlement does not use one fresh round. It averages all the rounds written during a window that starts at the first fresh round and lasts between 5 to 15 minutes.
- Window used: 10 minutes todo
- The window starts at the first fresh round, not at 9:30:00. If the first round arrives forty seconds after the bell, the window starts forty seconds after the bell.
- Rounds left over from before the bell are ignored, however many there are.
import { averageOracle } from "@/lib/protocol";
const avg = averageOracle(rounds, openAt, 10);
// null if no round has been written since the open
// otherwise { price, firstFreshAt, windowEndsAt, rounds }
The reasons for averaging are in Settlement formula.
If no fresh price arrives#
Then there is no oracle average, and without it there is no settlement. The sale stays open.
This happens when:
- the stock is halted at the open;
- the exchange is closed unexpectedly;
- the feed or the network has an outage.
Nothing is settled on a stale price to "unblock" the queue. The seller keeps the advance and waits for the balance; the pool keeps the stock and waits to sell it.
Corporate actions#
A split, a special dividend, a merger or a ticker change can make a feed pause, and can make the price jump for reasons that are not a change in value. A two-for-one split halves the price and nobody lost anything.
The tokenized stock may also adjust: the token contract may change how many shares each token represents.
For both reasons, a ticker is blocked while a corporate action is pending. No advance is sized on a price that is about to be redefined.
Earnings#
Earnings do not freeze the feed, but they are the most common cause of a large gap between the close and the open. They are handled by reducing the advance or blocking the ticker, not by the oracle. See Risk and safeguards.
The Base sequencer#
Base is a layer-two network. If its sequencer stops, prices on Base can lag the real world even though the feed contract looks normal. Chainlink publishes a sequencer uptime feed for this. The planned contracts check it, and wait for a grace period after the sequencer comes back before trusting any price: 3,600 seconds todo
What oracles do not protect against#
- A feed that is fresh and wrong.
- A real price that the pool cannot trade at. This is why settlement also uses the pool's realized TWAP and takes the lower of the two.
- A long outage. EVE waits; it does not guess.
These are listed with the other remaining risks in Risk and safeguards.