evm_oracle_state/lib.rs
1//! A real-time oracle state engine built on a forked-EVM state cache
2//! ([`evm_fork_cache`]).
3//!
4//! `evm-oracle-state` is the oracle domain crate layered over
5//! [`evm_fork_cache`]: the companion crate owns generic cache invalidation,
6//! reactive batching, and reorg reporting, while this crate owns
7//! oracle-specific metadata, event decoding, typed snapshots, staleness
8//! policy, reconciliation, and feed lookup. Chainlink-compatible feeds are
9//! registered through their proxy addresses; adapter-owned oracle families
10//! (Aave, Morpho, Euler, Pyth, RedStone) plug into the same typed state,
11//! hook, and overlay surfaces behind feature flags.
12//!
13//! The central contract:
14//!
15//! > Events are fast-path hints; proxy reads are authoritative.
16//!
17//! Oracle log events update typed state immediately, so simulations can
18//! observe a new answer with no RPC on the hot path; authoritative proxy
19//! reads confirm, correct, or repair that state afterwards:
20//!
21//! ```text
22//! oracle log event (AnswerUpdated / OCR1+OCR2 NewTransmission / adapter event)
23//! -> reactive handler decodes it; an OraclePriceUpdate hook fires immediately
24//! -> OracleTracker stores an EventPending typed snapshot
25//! -> OracleReadOverlay serves registered oracle view calls from typed state
26//! -> OracleReconciler reads the proxy latestRoundData()
27//! -> the snapshot becomes Confirmed or Corrected (or Stale / RequiresRepair)
28//! ```
29//!
30//! # Main types
31//!
32//! The internal modules are private; everything below is re-exported at the
33//! crate root:
34//!
35//! - [`OracleRuntime`] — the high-level runtime, built from a provider
36//! ([`OracleRuntime::builder`]) or an existing cache
37//! ([`OracleRuntime::cache_builder`]): [`ChainlinkFeed`] registrations,
38//! typed event callbacks, storage/code warmup, and the reactive
39//! install / refresh / uninstall lifecycle.
40//! - [`OracleAdapter`] — the cache-native registration path over an
41//! [`evm_fork_cache::cache::EvmCache`]: [`Feed`] builders,
42//! Multicall3-batched discovery reads, and skip-aware build reports.
43//! - [`OracleTracker`] — the typed state store: [`OracleSnapshot`] lookup
44//! with freshness classification, [`OraclePriceUpdate`] hook payloads, and
45//! reconciliation through [`OracleReconciler`].
46//! - [`OracleRegistry`] — feed registrations ([`FeedRegistration`],
47//! [`FeedId`]), aggregator and dependency bookkeeping, and feed-readiness
48//! reporting.
49//! - [`OracleReactiveHandler`] — dependency-aware log routing bridged into
50//! the [`evm_fork_cache`] reactive runtime; detected OCR1/OCR2 layouts get
51//! exact direct storage writes, everything else takes the conservative
52//! purge/refetch fallback.
53//! - [`OracleReadOverlay`] — serves registered oracle view calls
54//! (`latestRoundData()`, adapter reads such as Morpho `price()`, Euler
55//! `getQuote(...)`, Pyth `getPriceUnsafe(...)`) from typed state, so
56//! simulations see event prices before a follow-up RPC read completes.
57//! - [`PricePolicy`] / [`CheckedPrice`] — the typed price vocabulary
58//! ([`AssetId`], [`Denomination`], [`TokenAmount`]): promote raw oracle
59//! answers into policy-vetted values before liquidation or valuation
60//! decisions.
61//! - [`OracleAdapterPlugin`] — the extension seam for oracle families that
62//! are not plain Chainlink proxies: adapter-owned discovery plus an
63//! adapter-owned reactive handler over the same runtime.
64//! - Feature-gated adapters — [`AaveV3OracleAdapter`] (`aave`),
65//! [`MorphoBlueOracleAdapter`] (`morpho`), [`EulerOracleAdapter`]
66//! (`euler`), [`PythOracleAdapter`] (`pyth`), and
67//! [`RedstoneOracleAdapter`] (`redstone`).
68//!
69//! See the crate's `examples/local_oracle_lifecycle.rs` for the
70//! compile-checked quickstart: a deterministic, RPC-free walk through
71//! register → synthetic event → hook → overlay read → reconciliation.
72
73#![warn(missing_docs)]
74// docs.rs builds with `--cfg docsrs` (see `[package.metadata.docs.rs]`), which
75// enables nightly `doc_cfg`: every feature-gated item renders with its
76// "Available on crate feature … only" badge, derived automatically from the
77// existing `#[cfg]`s with no per-item annotations (auto-cfg is part of
78// `doc_cfg` since 1.92, rust-lang/rust#138907). Inert on stable builds.
79#![cfg_attr(docsrs, feature(doc_cfg))]
80
81/// Re-export of [`alloy_primitives`]: the `Address` / `U256` / `B256` / `Log`
82/// vocabulary this crate's API speaks.
83///
84/// Import from here (`evm_oracle_state::alloy_primitives`) to use exactly the
85/// version this crate's signatures expect without pinning `alloy-primitives`
86/// yourself.
87pub use alloy_primitives;
88/// Re-export of the [`evm_fork_cache`] companion crate: `EvmCache`, the
89/// reactive runtime, storage programs, and the typed errors that appear on
90/// this crate's cache and reactive seams.
91///
92/// `evm-fork-cache` is a 0.x **public dependency**: a semver-breaking bump
93/// there (e.g. 0.2 → 0.3) is necessarily a breaking release of this crate too,
94/// and the two are released in lockstep. Importing it through this re-export
95/// (`evm_oracle_state::evm_fork_cache`) guarantees the versions match.
96pub use evm_fork_cache;
97
98#[cfg(feature = "aave")]
99mod aave;
100mod adapter;
101mod cache_reader;
102mod code;
103mod config;
104mod derived;
105mod error;
106#[cfg(feature = "euler")]
107mod euler;
108mod events;
109mod feed;
110mod layout;
111mod mocking;
112#[cfg(feature = "morpho")]
113mod morpho;
114mod overlay;
115mod price;
116mod provider;
117#[cfg(feature = "pyth")]
118mod pyth;
119mod reactive;
120#[cfg(feature = "redstone")]
121mod redstone;
122mod registry;
123mod report;
124mod runtime;
125mod signal;
126mod state;
127mod storage;
128mod tracker;
129mod types;
130
131#[cfg(feature = "aave")]
132pub use aave::{AaveAsset, AaveV3OracleAdapter};
133pub use adapter::{
134 AdapterFuture, OracleAdapterId, OracleAdapterPlugin, OracleDiscoveredFeed,
135 OracleDiscoveryContext, OracleDiscoveryReport, OracleSourceDescriptor,
136 OracleTransformDescriptor,
137};
138pub use cache_reader::{
139 EvmCacheChainlinkReader, OracleAdapter, OracleAdapterBuildReport, OracleAdapterBuilder,
140 OracleAdapterFeedSkip, OracleAdapterSkipReason, OracleFeedSkip,
141};
142pub use code::{
143 OracleCodeInstall, OracleCodeInstallError, OracleCodeInstallMode, OracleCodeMismatch,
144 OracleCodePolicyViolation, OracleCodeRegistry, OracleCodeSeed, OracleCodeUnverifiable,
145 OracleCodeWarmupPolicy, OracleCodeWarmupReport,
146};
147pub use config::{FeedConfig, StalenessPolicy};
148pub use derived::OracleDerivedReader;
149pub use error::{
150 ChainlinkEventDecodeError, OracleConfigError, OracleError, OracleSignalDecodeError,
151};
152#[cfg(feature = "euler")]
153pub use euler::{EulerOracleAdapter, EulerQuotePair};
154pub use events::{
155 ANSWER_UPDATED_TOPIC, AnswerUpdated, NEW_ROUND_TOPIC, NewRound, OCR1_NEW_TRANSMISSION_TOPIC,
156 OCR2_NEW_TRANSMISSION_TOPIC, Ocr1NewTransmission, Ocr2NewTransmission, decode_answer_updated,
157 decode_new_round, decode_ocr1_new_transmission, decode_ocr2_new_transmission,
158};
159#[cfg(feature = "pyth")]
160pub use events::{
161 PYTH_PRICE_FEED_UPDATE_TOPIC, PythPriceFeedUpdate, decode_pyth_price_feed_update,
162};
163#[cfg(feature = "redstone")]
164pub use events::{REDSTONE_VALUE_UPDATE_TOPIC, RedstoneValueUpdate, decode_redstone_value_update};
165pub use feed::{Feed, FeedAccess, FeedBuilder, FeedBuilderState, FeedReady};
166pub use layout::{
167 AggregatorLayout, AggregatorLayoutConfidence, AggregatorLayoutEvidence,
168 classify_type_and_version,
169};
170pub use mocking::{OracleMockError, OracleMockReport, OracleOverlayMockExt, OraclePriceMock};
171#[cfg(feature = "morpho")]
172pub use morpho::{MorphoBlueMarket, MorphoBlueOracleAdapter, MorphoMarketParams};
173pub use overlay::OracleReadOverlay;
174pub use price::{CheckedPrice, OraclePrice, PricePolicy, PricePolicyViolation};
175pub use provider::{ChainlinkFeedProvider, OracleBlockRef, ProviderFuture};
176#[cfg(feature = "pyth")]
177pub use pyth::{PythFeed, PythOracleAdapter, PythReactiveHandler};
178pub use reactive::OracleReactiveHandler;
179#[cfg(feature = "redstone")]
180pub use redstone::{
181 RedstoneFeed, RedstoneMultiFeedStorageAdapter, RedstoneOracleAdapter,
182 RedstonePriceFeedsStorageAdapter, RedstoneReactiveHandler,
183};
184pub use registry::{
185 AggregatorChange, EulerQuoteLeg, FeedId, FeedRegistration, FeedSource, MorphoChainlinkFeed,
186 MorphoChainlinkFeedRole, OracleDependency, OracleFeedReadinessReport, OracleRegistry,
187};
188pub use report::{
189 OracleBatchReport, OracleChangeImpact, OracleFeedChange, OracleFeedChangeKind, OracleIncident,
190};
191pub use runtime::{
192 ChainlinkFeed, OracleCacheRuntimeBuildReport, OracleCacheRuntimeBuilder,
193 OracleColdStartWarmupReport, OracleReactiveInstallReport, OracleReactiveRefreshReport,
194 OracleReactiveUninstallReport, OracleRuntime, OracleRuntimeBuilder,
195 OracleRuntimeMutationReport, OracleStorageWarmupFailure, OracleStorageWarmupMode,
196 OracleStorageWarmupReport,
197};
198pub use signal::{
199 ORACLE_LEGACY_ANSWER_UPDATED_KIND, ORACLE_SIGNAL_NAMESPACE, OracleSignal, OracleSignalKind,
200};
201pub use state::{
202 FeedMetadata, OracleFeedStatus, OracleRoundStatus, OracleSnapshot, OracleValueSource,
203 OracleValueStatus, RoundData,
204};
205pub use storage::{
206 ChainlinkOcr1StorageAdapter, ChainlinkOcr2StorageAdapter, Ocr1TransmissionStorageUpdate,
207 Ocr2TransmissionStorageUpdate, OracleStorageAdapter, OracleStorageEffect, OracleStorageError,
208 OracleStorageSync,
209};
210pub use tracker::{
211 DerivedReconcileFailure, DerivedReconcileReport, OracleAggregatorChanged, OracleHookEvent,
212 OraclePriceConfirmed, OraclePriceCorrected, OraclePriceStale, OraclePriceUpdate,
213 OracleReconciler, OracleReconciliationKind, OracleReconciliationRequest,
214 OracleReconciliationResult, OracleTracker, OracleUpdate, ReconcileReport,
215};
216pub use types::{AssetId, Denomination, TokenAmount, ValuedAmount};
217
218/// Compiles the README's code samples as doctests so the documented snippets
219/// cannot drift from the real API. Exists only under `cfg(doctest)` — it is
220/// never part of the built crate or the rendered docs. Gated on every adapter
221/// feature because the README demonstrates the Aave / Morpho / Euler / Pyth /
222/// RedStone surfaces; the adapter-features test run (local + CI) compiles it.
223#[cfg(all(
224 doctest,
225 feature = "aave",
226 feature = "morpho",
227 feature = "euler",
228 feature = "pyth",
229 feature = "redstone"
230))]
231#[doc = include_str!("../README.md")]
232pub struct ReadmeDoctests;