use std::{borrow::Cow, collections::BTreeMap, future::Future, pin::Pin, sync::Arc};
use alloy_network::Ethereum;
use alloy_primitives::{Address, I256};
use evm_fork_cache::{cache::EvmCache, reactive::ReactiveHandler};
use crate::{FeedRegistration, OracleAdapterFeedSkip, OracleError, OracleStorageSync, RoundData};
pub type AdapterFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T, OracleError>> + 'a>>;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OracleAdapterId(Cow<'static, str>);
impl OracleAdapterId {
pub fn new(id: impl Into<Cow<'static, str>>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
self.0.as_ref()
}
}
impl std::fmt::Display for OracleAdapterId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl AsRef<str> for OracleAdapterId {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[non_exhaustive]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub enum OracleTransformDescriptor {
#[default]
Identity,
PriceCap {
cap: I256,
},
Custom {
adapter_id: OracleAdapterId,
kind: Cow<'static, str>,
metadata: BTreeMap<String, String>,
},
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OracleSourceDescriptor {
pub adapter_id: OracleAdapterId,
pub source_kind: Cow<'static, str>,
pub user_facing_proxy: Address,
pub read_proxy: Address,
pub event_source: Address,
pub transform: OracleTransformDescriptor,
pub metadata: BTreeMap<String, String>,
}
impl OracleSourceDescriptor {
pub fn new(
adapter_id: OracleAdapterId,
source_kind: impl Into<Cow<'static, str>>,
user_facing_proxy: Address,
read_proxy: Address,
event_source: Address,
) -> Self {
Self {
adapter_id,
source_kind: source_kind.into(),
user_facing_proxy,
read_proxy,
event_source,
transform: OracleTransformDescriptor::Identity,
metadata: BTreeMap::new(),
}
}
pub fn transform(mut self, transform: OracleTransformDescriptor) -> Self {
self.transform = transform;
self
}
pub fn metadata(mut self, metadata: BTreeMap<String, String>) -> Self {
self.metadata = metadata;
self
}
pub fn metadata_entry(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.metadata.insert(key.into(), value.into());
self
}
}
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct OracleDiscoveredFeed {
pub registration: FeedRegistration,
pub round: RoundData,
}
impl OracleDiscoveredFeed {
pub fn new(registration: FeedRegistration, round: RoundData) -> Self {
Self {
registration,
round,
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug, Default)]
pub struct OracleDiscoveryReport {
pub feeds: Vec<OracleDiscoveredFeed>,
pub skipped: Vec<OracleAdapterFeedSkip>,
}
impl OracleDiscoveryReport {
pub fn new() -> Self {
Self::default()
}
pub fn with_feed(mut self, feed: OracleDiscoveredFeed) -> Self {
self.feeds.push(feed);
self
}
pub fn with_skip(mut self, skip: OracleAdapterFeedSkip) -> Self {
self.skipped.push(skip);
self
}
}
pub struct OracleDiscoveryContext<'a> {
pub cache: &'a mut EvmCache,
pub now_timestamp: u64,
}
pub trait OracleAdapterPlugin: Send + Sync {
fn adapter_id(&self) -> OracleAdapterId;
fn discover<'a>(
&'a self,
ctx: OracleDiscoveryContext<'a>,
) -> AdapterFuture<'a, OracleDiscoveryReport>;
fn reactive_handler(
&self,
registrations: Vec<FeedRegistration>,
storage_sync: OracleStorageSync,
) -> Arc<dyn ReactiveHandler<Ethereum>>;
}