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 #[allow(clippy::too_many_arguments)]
61 pub fn price(
62 &self,
63 pair_id: &str,
64 sell_token: Address,
65 buy_token: Address,
66 amounts: Vec<U256>,
67 overwrites: Option<HashMap<Address, Overwrites>>,
68 caller: Option<Address>,
69 block_overrides: Option<BlockEnvOverrides>,
70 ) -> Result<Vec<f64>, SimulationError> {
71 let args = (string_to_bytes32(pair_id)?, sell_token, buy_token, amounts);
72 let selector = "price(bytes32,address,address,uint256[])";
73
74 let res = self
75 .call(selector, args, overwrites, caller, U256::from(0u64), None, block_overrides)?
76 .return_value;
77
78 let decoded: PriceReturn = PriceReturn::abi_decode(&res).map_err(|e| {
79 SimulationError::FatalError(format!("Failed to decode price return value: {e:?}"))
80 })?;
81
82 let price = self.calculate_price(decoded)?;
83 Ok(price)
84 }
85
86 #[allow(clippy::too_many_arguments)]
87 pub fn swap(
88 &self,
89 pair_id: &str,
90 sell_token: Address,
91 buy_token: Address,
92 is_buy: bool,
93 amount: U256,
94 overwrites: Option<HashMap<Address, HashMap<U256, U256>>>,
95 block_overrides: Option<BlockEnvOverrides>,
96 ) -> Result<(Trade, HashMap<Address, StateUpdate>), SimulationError> {
97 let args = (string_to_bytes32(pair_id)?, sell_token, buy_token, is_buy, amount);
98 let selector = "swap(bytes32,address,address,uint8,uint256)";
99
100 let res =
101 self.call(selector, args, overwrites, None, U256::from(0u64), None, block_overrides)?;
102
103 let decoded: SwapReturn = SwapReturn::abi_decode(&res.return_value).map_err(|_| {
104 SimulationError::FatalError(format!(
105 "Adapter swap call failed: Failed to decode return value. Expected amount, gas, and price elements in the format (U256, U256, (U256, U256)). Found {:?}",
106 &res.return_value[..],
107 ))
108 })?;
109
110 let (received_amount, gas_used, price_elements) = decoded;
111
112 let price = self
113 .calculate_price(vec![price_elements])?
114 .first()
115 .cloned()
116 .ok_or_else(|| {
117 SimulationError::FatalError(
118 "Adapter swap call failed: An empty price list was returned".into(),
119 )
120 })?;
121
122 Ok((Trade { received_amount, gas_used, price }, res.simulation_result.state_updates))
123 }
124
125 pub fn get_limits(
126 &self,
127 pair_id: &str,
128 sell_token: Address,
129 buy_token: Address,
130 overwrites: Option<HashMap<Address, HashMap<U256, U256>>>,
131 block_overrides: Option<BlockEnvOverrides>,
132 ) -> Result<(U256, U256), SimulationError> {
133 let args = (string_to_bytes32(pair_id)?, sell_token, buy_token);
134 let selector = "getLimits(bytes32,address,address)";
135
136 let res = self
137 .call(selector, args, overwrites, None, U256::from(0u64), None, block_overrides)?
138 .return_value;
139
140 let decoded: LimitsReturn = LimitsReturn::abi_decode(&res).map_err(|e| {
141 SimulationError::FatalError(format!(
142 "Adapter get_limits call failed: Failed to decode return value: {e:?}"
143 ))
144 })?;
145
146 Ok((decoded[0], decoded[1]))
147 }
148
149 pub fn get_capabilities(
150 &self,
151 pair_id: &str,
152 sell_token: Address,
153 buy_token: Address,
154 ) -> Result<HashSet<Capability>, SimulationError> {
155 let args = (string_to_bytes32(pair_id)?, sell_token, buy_token);
156 let selector = "getCapabilities(bytes32,address,address)";
157
158 let res = self
159 .call(selector, args, None, None, U256::from(0u64), None, None)?
160 .return_value;
161 let decoded: CapabilitiesReturn = CapabilitiesReturn::abi_decode(&res).map_err(|e| {
162 SimulationError::FatalError(format!(
163 "Adapter get_capabilities call failed: Failed to decode return value: {e:?}"
164 ))
165 })?;
166
167 let capabilities: HashSet<Capability> = decoded
168 .into_iter()
169 .filter_map(|value| Capability::from_u256(value).ok())
170 .collect();
171
172 Ok(capabilities)
173 }
174
175 #[allow(dead_code)]
176 pub fn min_gas_usage(&self) -> Result<u64, SimulationError> {
177 let args = ();
178 let selector = "minGasUsage()";
179
180 let res = self
181 .call(selector, args, None, None, U256::from(0u64), None, None)?
182 .return_value;
183
184 let decoded: MinGasUsageReturn = MinGasUsageReturn::abi_decode(&res).map_err(|e| {
185 SimulationError::FatalError(format!(
186 "Adapter min gas usage call failed: Failed to decode return value: {e:?}"
187 ))
188 })?;
189 decoded
190 .try_into()
191 .map_err(|_| SimulationError::FatalError("Decoded value exceeds u64 range".to_string()))
192 }
193
194 fn calculate_price(&self, fractions: Vec<(U256, U256)>) -> Result<Vec<f64>, SimulationError> {
195 fractions
196 .into_iter()
197 .map(|(numerator, denominator)| {
198 if denominator.is_zero() {
199 Err(SimulationError::FatalError(
200 "Adapter price calculation failed: Denominator is zero".to_string(),
201 ))
202 } else {
203 let num_f64 = u256_to_f64(numerator)?;
204 let den_f64 = u256_to_f64(denominator)?;
205 Ok(num_f64 / den_f64)
206 }
207 })
208 .collect()
209 }
210}