use std::{
collections::{HashMap, HashSet},
fmt::Debug,
};
use alloy::{
primitives::{Address, U256},
sol_types::SolValue,
};
use revm::DatabaseRef;
use tycho_common::simulation::errors::SimulationError;
use super::{
erc20_token::Overwrites, models::Capability, tycho_simulation_contract::TychoSimulationContract,
};
use crate::evm::{
account_storage::StateUpdate,
engine_db::engine_db_interface::EngineDatabaseInterface,
protocol::{u256_num::u256_to_f64, vm::utils::string_to_bytes32},
};
#[derive(Debug)]
pub struct Trade {
pub received_amount: U256,
pub gas_used: U256,
pub price: f64,
}
type PriceReturn = Vec<(U256, U256)>;
type SwapReturn = (U256, U256, (U256, U256));
type LimitsReturn = Vec<U256>;
type CapabilitiesReturn = Vec<U256>;
type MinGasUsageReturn = U256;
impl<D: EngineDatabaseInterface + Clone + Debug> TychoSimulationContract<D>
where
<D as DatabaseRef>::Error: Debug,
<D as EngineDatabaseInterface>::Error: Debug,
{
pub fn price(
&self,
pair_id: &str,
sell_token: Address,
buy_token: Address,
amounts: Vec<U256>,
overwrites: Option<HashMap<Address, Overwrites>>,
) -> Result<Vec<f64>, SimulationError> {
let args = (string_to_bytes32(pair_id)?, sell_token, buy_token, amounts);
let selector = "price(bytes32,address,address,uint256[])";
let res = self
.call(selector, args, overwrites, None, U256::from(0u64), None)?
.return_value;
let decoded: PriceReturn = PriceReturn::abi_decode(&res).map_err(|e| {
SimulationError::FatalError(format!("Failed to decode price return value: {e:?}"))
})?;
let price = self.calculate_price(decoded)?;
Ok(price)
}
#[allow(clippy::too_many_arguments)]
pub fn swap(
&self,
pair_id: &str,
sell_token: Address,
buy_token: Address,
is_buy: bool,
amount: U256,
overwrites: Option<HashMap<Address, HashMap<U256, U256>>>,
) -> Result<(Trade, HashMap<Address, StateUpdate>), SimulationError> {
let args = (string_to_bytes32(pair_id)?, sell_token, buy_token, is_buy, amount);
let selector = "swap(bytes32,address,address,uint8,uint256)";
let res = self.call(selector, args, overwrites, None, U256::from(0u64), None)?;
let decoded: SwapReturn = SwapReturn::abi_decode(&res.return_value).map_err(|_| {
SimulationError::FatalError(format!(
"Adapter swap call failed: Failed to decode return value. Expected amount, gas, and price elements in the format (U256, U256, (U256, U256)). Found {:?}",
&res.return_value[..],
))
})?;
let (received_amount, gas_used, price_elements) = decoded;
let price = self
.calculate_price(vec![price_elements])?
.first()
.cloned()
.ok_or_else(|| {
SimulationError::FatalError(
"Adapter swap call failed: An empty price list was returned".into(),
)
})?;
Ok((Trade { received_amount, gas_used, price }, res.simulation_result.state_updates))
}
pub fn get_limits(
&self,
pair_id: &str,
sell_token: Address,
buy_token: Address,
overwrites: Option<HashMap<Address, HashMap<U256, U256>>>,
) -> Result<(U256, U256), SimulationError> {
let args = (string_to_bytes32(pair_id)?, sell_token, buy_token);
let selector = "getLimits(bytes32,address,address)";
let res = self
.call(selector, args, overwrites, None, U256::from(0u64), None)?
.return_value;
let decoded: LimitsReturn = LimitsReturn::abi_decode(&res).map_err(|e| {
SimulationError::FatalError(format!(
"Adapter get_limits call failed: Failed to decode return value: {e:?}"
))
})?;
Ok((decoded[0], decoded[1]))
}
pub fn get_capabilities(
&self,
pair_id: &str,
sell_token: Address,
buy_token: Address,
) -> Result<HashSet<Capability>, SimulationError> {
let args = (string_to_bytes32(pair_id)?, sell_token, buy_token);
let selector = "getCapabilities(bytes32,address,address)";
let res = self
.call(selector, args, None, None, U256::from(0u64), None)?
.return_value;
let decoded: CapabilitiesReturn = CapabilitiesReturn::abi_decode(&res).map_err(|e| {
SimulationError::FatalError(format!(
"Adapter get_capabilities call failed: Failed to decode return value: {e:?}"
))
})?;
let capabilities: HashSet<Capability> = decoded
.into_iter()
.filter_map(|value| Capability::from_u256(value).ok())
.collect();
Ok(capabilities)
}
#[allow(dead_code)]
pub fn min_gas_usage(&self) -> Result<u64, SimulationError> {
let args = ();
let selector = "minGasUsage()";
let res = self
.call(selector, args, None, None, U256::from(0u64), None)?
.return_value;
let decoded: MinGasUsageReturn = MinGasUsageReturn::abi_decode(&res).map_err(|e| {
SimulationError::FatalError(format!(
"Adapter min gas usage call failed: Failed to decode return value: {e:?}"
))
})?;
decoded
.try_into()
.map_err(|_| SimulationError::FatalError("Decoded value exceeds u64 range".to_string()))
}
fn calculate_price(&self, fractions: Vec<(U256, U256)>) -> Result<Vec<f64>, SimulationError> {
fractions
.into_iter()
.map(|(numerator, denominator)| {
if denominator.is_zero() {
Err(SimulationError::FatalError(
"Adapter price calculation failed: Denominator is zero".to_string(),
))
} else {
let num_f64 = u256_to_f64(numerator)?;
let den_f64 = u256_to_f64(denominator)?;
Ok(num_f64 / den_f64)
}
})
.collect()
}
}