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