tycho_simulation/evm/protocol/vm/
adapter_contract.rs1use std::{
2 collections::{HashMap, HashSet},
3 fmt::Debug,
4};
5
6use alloy::{
7 primitives::{Address, U256},
8 sol_types::SolValue,
9};
10use revm::DatabaseRef;
11use tycho_common::simulation::errors::SimulationError;
12
13use super::{
14 erc20_token::Overwrites, models::Capability, tycho_simulation_contract::TychoSimulationContract,
15};
16use crate::evm::{
17 account_storage::StateUpdate,
18 engine_db::engine_db_interface::EngineDatabaseInterface,
19 protocol::{u256_num::u256_to_f64, vm::utils::string_to_bytes32},
20 simulation::BlockEnvOverrides,
21};
22
23#[derive(Debug)]
24pub struct Trade {
25 pub received_amount: U256,
26 pub gas_used: U256,
27 pub price: f64,
28}
29
30type PriceReturn = Vec<(U256, U256)>;
35type SwapReturn = (U256, U256, (U256, U256));
36type LimitsReturn = Vec<U256>;
37type CapabilitiesReturn = Vec<U256>;
38type MinGasUsageReturn = U256;
39
40impl<D: EngineDatabaseInterface + Clone + Debug> TychoSimulationContract<D>
54where
55 <D as DatabaseRef>::Error: Debug,
56 <D as EngineDatabaseInterface>::Error: Debug,
57{
58 pub fn price(
59 &self,
60 pair_id: &str,
61 sell_token: Address,
62 buy_token: Address,
63 amounts: Vec<U256>,
64 overwrites: Option<HashMap<Address, Overwrites>>,
65 block_overrides: Option<BlockEnvOverrides>,
66 ) -> Result<Vec<f64>, SimulationError> {
67 let args = (string_to_bytes32(pair_id)?, sell_token, buy_token, amounts);
68 let selector = "price(bytes32,address,address,uint256[])";
69
70 let res = self
71 .call(selector, args, overwrites, None, U256::from(0u64), None, block_overrides)?
72 .return_value;
73
74 let decoded: PriceReturn = PriceReturn::abi_decode(&res).map_err(|e| {
75 SimulationError::FatalError(format!("Failed to decode price return value: {e:?}"))
76 })?;
77
78 let price = self.calculate_price(decoded)?;
79 Ok(price)
80 }
81
82 #[allow(clippy::too_many_arguments)]
83 pub fn swap(
84 &self,
85 pair_id: &str,
86 sell_token: Address,
87 buy_token: Address,
88 is_buy: bool,
89 amount: U256,
90 overwrites: Option<HashMap<Address, HashMap<U256, U256>>>,
91 block_overrides: Option<BlockEnvOverrides>,
92 ) -> Result<(Trade, HashMap<Address, StateUpdate>), SimulationError> {
93 let args = (string_to_bytes32(pair_id)?, sell_token, buy_token, is_buy, amount);
94 let selector = "swap(bytes32,address,address,uint8,uint256)";
95
96 let res =
97 self.call(selector, args, overwrites, None, U256::from(0u64), None, block_overrides)?;
98
99 let decoded: SwapReturn = SwapReturn::abi_decode(&res.return_value).map_err(|_| {
100 SimulationError::FatalError(format!(
101 "Adapter swap call failed: Failed to decode return value. Expected amount, gas, and price elements in the format (U256, U256, (U256, U256)). Found {:?}",
102 &res.return_value[..],
103 ))
104 })?;
105
106 let (received_amount, gas_used, price_elements) = decoded;
107
108 let price = self
109 .calculate_price(vec![price_elements])?
110 .first()
111 .cloned()
112 .ok_or_else(|| {
113 SimulationError::FatalError(
114 "Adapter swap call failed: An empty price list was returned".into(),
115 )
116 })?;
117
118 Ok((Trade { received_amount, gas_used, price }, res.simulation_result.state_updates))
119 }
120
121 pub fn get_limits(
122 &self,
123 pair_id: &str,
124 sell_token: Address,
125 buy_token: Address,
126 overwrites: Option<HashMap<Address, HashMap<U256, U256>>>,
127 block_overrides: Option<BlockEnvOverrides>,
128 ) -> Result<(U256, U256), SimulationError> {
129 let args = (string_to_bytes32(pair_id)?, sell_token, buy_token);
130 let selector = "getLimits(bytes32,address,address)";
131
132 let res = self
133 .call(selector, args, overwrites, None, U256::from(0u64), None, block_overrides)?
134 .return_value;
135
136 let decoded: LimitsReturn = LimitsReturn::abi_decode(&res).map_err(|e| {
137 SimulationError::FatalError(format!(
138 "Adapter get_limits call failed: Failed to decode return value: {e:?}"
139 ))
140 })?;
141
142 Ok((decoded[0], decoded[1]))
143 }
144
145 pub fn get_capabilities(
146 &self,
147 pair_id: &str,
148 sell_token: Address,
149 buy_token: Address,
150 ) -> Result<HashSet<Capability>, SimulationError> {
151 let args = (string_to_bytes32(pair_id)?, sell_token, buy_token);
152 let selector = "getCapabilities(bytes32,address,address)";
153
154 let res = self
155 .call(selector, args, None, None, U256::from(0u64), None, None)?
156 .return_value;
157 let decoded: CapabilitiesReturn = CapabilitiesReturn::abi_decode(&res).map_err(|e| {
158 SimulationError::FatalError(format!(
159 "Adapter get_capabilities call failed: Failed to decode return value: {e:?}"
160 ))
161 })?;
162
163 let capabilities: HashSet<Capability> = decoded
164 .into_iter()
165 .filter_map(|value| Capability::from_u256(value).ok())
166 .collect();
167
168 Ok(capabilities)
169 }
170
171 #[allow(dead_code)]
172 pub fn min_gas_usage(&self) -> Result<u64, SimulationError> {
173 let args = ();
174 let selector = "minGasUsage()";
175
176 let res = self
177 .call(selector, args, None, None, U256::from(0u64), None, None)?
178 .return_value;
179
180 let decoded: MinGasUsageReturn = MinGasUsageReturn::abi_decode(&res).map_err(|e| {
181 SimulationError::FatalError(format!(
182 "Adapter min gas usage call failed: Failed to decode return value: {e:?}"
183 ))
184 })?;
185 decoded
186 .try_into()
187 .map_err(|_| SimulationError::FatalError("Decoded value exceeds u64 range".to_string()))
188 }
189
190 fn calculate_price(&self, fractions: Vec<(U256, U256)>) -> Result<Vec<f64>, SimulationError> {
191 fractions
192 .into_iter()
193 .map(|(numerator, denominator)| {
194 if denominator.is_zero() {
195 Err(SimulationError::FatalError(
196 "Adapter price calculation failed: Denominator is zero".to_string(),
197 ))
198 } else {
199 let num_f64 = u256_to_f64(numerator)?;
200 let den_f64 = u256_to_f64(denominator)?;
201 Ok(num_f64 / den_f64)
202 }
203 })
204 .collect()
205 }
206}