Appearance
Oracle & History
The oracle system provides the floating rate data that drives funding settlement across all positions. It consists of two components: the RateOracle (current state) and OracleHistory (historical record).
RateOracle
Each oracle tracks a cumulative funding index for a specific rate source - perp funding rates, borrow APRs, staking yields, or any other variable rate. The index is a signed cumulative integral of the instantaneous floating rate over time.
Updates
Oracle updates advance the index by providing a new cumulative value and timestamp. Updates can be permissioned (pool authority only) or permissionless depending on the rate source and configuration.
Each update must satisfy:
- Strictly increasing time: The new timestamp must be after the last update
- Sanity bounds: The rate change is capped at configured maximums - both per-second and per-update. If the proposed change exceeds the bound, it is capped (not rejected), preserving direction while limiting magnitude.
Capping rather than rejecting ensures that oracle updates always advance the state forward, even if the underlying rate moves faster than expected. The protocol continues to operate with a bounded approximation rather than stalling.
Freshness
An oracle is considered fresh if the time since its last update is within max_staleness_secs. Operations that require fresh rate data (like swaps with deviation checks) will fail if the oracle is stale. However, expired position settlement using historical proofs does not require oracle freshness.
Volatility Tracking
On each update, the oracle computes the annualized instantaneous rate and updates an EWMA variance (exponentially weighted moving average of squared rate changes). This variance estimate is used by the volatility fee component (see Fees) - higher recent volatility leads to higher trading fees, protecting LPs during turbulent conditions.
Oracle History
Every oracle update automatically appends a leaf to an append-only Merkle tree that records the historical evolution of the funding index. This history enables trustless verification of past oracle values - critical for settling positions that expired while the oracle has since moved on.
Leaf Structure
Each leaf represents a time segment of the oracle's evolution:
| Field | Description |
|---|---|
ts_start | Start timestamp of the interval |
ts_end | End timestamp of the interval |
index_start | Oracle index at ts_start |
index_end | Oracle index at ts_end |
To recover the oracle index at any timestamp within a leaf's interval, the protocol uses linear interpolation between the start and end values.
Tree Design
The Merkle tree is:
- Append-only: Leaves are added sequentially, never modified or deleted
- Configurable depth: Tree depth (3–16) determines capacity - from 8 to 65,536 leaves per tree
- Canopy-cached: Upper levels of the tree are stored on-chain to reduce proof sizes for settlement. Canopy depth (0–7) determines how many levels are cached.
The tree uses a rightmost proof pattern (similar to SPL Account Compression) that allows internal proof computation for appends without requiring external proof data. This means oracle updates can append to the history in a single transaction without off-chain proof generation.
Tree Rotation
When a tree fills to capacity, it is sealed (marked read-only) and a new tree is created for subsequent appends. The oracle tracks the active tree index, which increments on each rotation. Each tree is a separate PDA derived from the oracle pubkey and tree index:
Seeds: ["oracle_history", oracle_pubkey, tree_index_le_bytes]Sealed trees remain available for historical proofs indefinitely - they are never deleted.
Settlement Proofs
When settling an expired position, the caller provides a Merkle proof that:
- Identifies a leaf whose time interval contains the position's expiry timestamp
- Proves the leaf is part of the history tree (valid Merkle path to the root)
The protocol verifies the proof on-chain, extracts the leaf data, and interpolates to find the exact oracle index at the expiry time. This is how positions are settled at their precise expiry rate regardless of when the settlement transaction occurs.