use alloy::{eips::BlockId, primitives::U256, providers::Provider};
use crate::{abi::dex, types};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ContractVersion {
major: u64,
minor: u64,
patch: u64,
}
impl ContractVersion {
pub const V2_GETTERS: Self = Self { major: 1, minor: 7, patch: 3 };
pub const BUILDER_CODES: Self = Self { major: 1, minor: 7, patch: 4 };
pub const fn new(major: u64, minor: u64, patch: u64) -> Self { Self { major, minor, patch } }
pub const fn major(&self) -> u64 { self.major }
pub const fn minor(&self) -> u64 { self.minor }
pub const fn patch(&self) -> u64 { self.patch }
}
impl std::fmt::Display for ContractVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "v1.{}.{}.{}", self.major, self.minor, self.patch)
}
}
#[derive(Clone, Copy, Debug)]
pub struct ContractFeatures {
version: Option<ContractVersion>,
v2_state_getters: bool,
keyed_fee_schedules: bool,
builder_attribution: bool,
perpetual_discovery: bool,
}
impl ContractFeatures {
pub fn current() -> Self {
Self {
version: None,
v2_state_getters: true,
keyed_fee_schedules: true,
builder_attribution: true,
perpetual_discovery: true,
}
}
pub fn of(version: ContractVersion) -> Self {
let builder_codes = version >= ContractVersion::BUILDER_CODES;
Self {
version: Some(version),
v2_state_getters: version >= ContractVersion::V2_GETTERS,
keyed_fee_schedules: builder_codes,
builder_attribution: builder_codes,
perpetual_discovery: builder_codes,
}
}
pub fn version(&self) -> Option<ContractVersion> { self.version }
pub fn v2_state_getters(&self) -> bool { self.v2_state_getters }
pub fn keyed_fee_schedules(&self) -> bool { self.keyed_fee_schedules }
pub fn builder_attribution(&self) -> bool { self.builder_attribution }
pub fn perpetual_discovery(&self) -> bool { self.perpetual_discovery }
pub(crate) async fn probe<P: Provider>(
instance: &dex::Exchange::ExchangeInstance<P>,
block_id: BlockId,
probe_perpetual: Option<types::PerpetualId>,
) -> Self {
if let Ok(v) = instance
.getContractVersion()
.block(block_id)
.call()
.await
.map(|v| ContractVersion::new(v.major.to(), v.minor.to(), v.patch.to()))
{
return Self::of(v);
}
let mut features = Self {
version: None,
v2_state_getters: true,
keyed_fee_schedules: false,
builder_attribution: false,
perpetual_discovery: false,
};
if let Some(perp_id) = probe_perpetual {
features
.probe_v2_state_getters(instance, block_id, perp_id)
.await;
}
features
}
pub(crate) async fn probe_v2_state_getters<P: Provider>(
&mut self,
instance: &dex::Exchange::ExchangeInstance<P>,
block_id: BlockId,
perp_id: types::PerpetualId,
) {
if self.version.is_some() {
return;
}
self.v2_state_getters = instance
.getPerpetualInfoV2(U256::from(perp_id))
.block(block_id)
.call()
.await
.is_ok();
}
pub(crate) fn observe_version(&mut self, version: ContractVersion) {
*self = Self::of(version);
}
}
impl Default for ContractFeatures {
fn default() -> Self { Self::current() }
}
impl std::fmt::Display for ContractFeatures {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.version {
Some(version) => write!(f, "{version}"),
None => write!(
f,
"unversioned ({})",
if self.v2_state_getters { "V2 getters" } else { "V0 getters" },
),
}
}
}