use hashbrown::HashMap;
use alloy::primitives::{Address, U256};
use serde::{Deserialize, Deserializer};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Amount(pub U256);
impl<'de> Deserialize<'de> for Amount {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
#[serde(untagged)]
enum Raw {
Num(u64),
Str(String),
}
let u = match Raw::deserialize(d)? {
Raw::Num(n) => U256::from(n),
Raw::Str(s) => s.parse::<U256>().map_err(serde::de::Error::custom)?,
};
Ok(Amount(u))
}
}
impl std::fmt::Display for Amount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Debug, Deserialize)]
pub struct TickJson {
pub liquidity_net: String,
pub liquidity_gross: String,
}
#[derive(Clone, Debug, Default, Deserialize)]
pub struct PoolData {
#[serde(default)]
pub address: Option<Address>,
#[serde(default)]
pub token0: Option<Address>,
#[serde(default)]
pub token1: Option<Address>,
#[serde(default)]
pub pool_manager: Option<Address>,
#[serde(default)]
pub pool_id: Option<String>,
#[serde(default)]
pub currency0: Option<Address>,
#[serde(default)]
pub currency1: Option<Address>,
#[serde(default)]
pub hooks: Option<Address>,
#[serde(default)]
pub tick_spacing: Option<i32>,
#[serde(default)]
pub fee_token0: Option<u32>,
#[serde(default)]
pub fee_currency0: Option<u32>,
#[serde(default)]
pub tick: Option<i32>,
#[serde(default)]
pub sqrt_price_x96: Option<Amount>,
#[serde(default)]
pub liquidity: Option<Amount>,
#[serde(default)]
pub protocol_fee: Option<u32>,
#[serde(default)]
pub liquidity_update_block: Option<u64>,
#[serde(default)]
pub reserve0: Option<Amount>,
#[serde(default)]
pub reserve1: Option<Amount>,
#[serde(default)]
pub fee_gamma: Option<u64>,
#[serde(default)]
pub fee_denom: Option<u64>,
#[serde(default)]
pub family: Option<String>,
#[serde(default)]
pub tick_data: HashMap<String, TickJson>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct PathHop {
pub hop: usize,
pub pool: String,
pub zero_for_one: bool,
}
#[derive(Clone, Debug, Default, Deserialize)]
pub struct RecordedSolve {
#[serde(default)]
pub optimal_input: Option<Amount>,
#[serde(default)]
pub hop_outputs: Vec<Amount>,
#[serde(default)]
pub sim_bucket: Option<String>,
#[serde(default)]
pub v4_hop_index: Option<usize>,
#[serde(default)]
pub v4_zero_for_one: Option<bool>,
#[serde(default)]
pub v4_input: Option<Amount>,
#[serde(default)]
pub v4_predicted_output: Option<Amount>,
#[serde(default)]
pub v4_onchain: Option<String>,
#[serde(default)]
pub v2_hop_index: Option<usize>,
#[serde(default)]
pub v2_input: Option<Amount>,
#[serde(default)]
pub v2_predicted: Option<Amount>,
#[serde(default)]
pub v2_actual: Option<Amount>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct PathFixture {
#[serde(default)]
pub target_block: Option<u64>,
#[serde(default)]
pub pools: HashMap<String, PoolData>,
#[serde(default)]
pub path: Vec<PathHop>,
#[serde(default)]
pub recorded_solve: RecordedSolve,
}
impl PathFixture {
pub fn load(path: &str) -> Result<Self, String> {
let text = std::fs::read_to_string(path).map_err(|e| format!("read {path}: {e}"))?;
serde_json::from_str(&text).map_err(|e| format!("parse {path}: {e}"))
}
}