1use crate::state_readers::UniswapV2EVMStateReader;
2use alloy::primitives::{Address, Bytes, U256};
3use alloy::providers::{Network, Provider};
4use alloy::rpc::types::BlockNumberOrTag;
5use alloy::sol_types::SolInterface;
6use eyre::{eyre, ErrReport, Result};
7use kabu_defi_abi::uniswap2::IUniswapV2Pair;
8use kabu_defi_abi::IERC20;
9use kabu_defi_address_book::FactoryAddress;
10use kabu_evm_db::KabuDBError;
11use kabu_types_entities::required_state::RequiredState;
12use kabu_types_entities::{Pool, PoolAbiEncoder, PoolClass, PoolId, PoolProtocol, PreswapRequirement, SwapDirection};
13use lazy_static::lazy_static;
14use revm::DatabaseRef;
15use std::any::Any;
16use std::ops::Div;
17use tracing::debug;
18
19lazy_static! {
20 static ref U112_MASK: U256 = (U256::from(1) << 112) - U256::from(1);
21 static ref U256_ONE: U256 = U256::from(1);
22}
23#[allow(dead_code)]
24#[derive(Clone)]
25pub struct UniswapV2Pool {
26 address: Address,
27 token0: Address,
28 token1: Address,
29 factory: Address,
30 protocol: PoolProtocol,
31 fee: U256,
32 encoder: UniswapV2PoolAbiEncoder,
33 reserves_cell: Option<U256>,
34 liquidity0: U256,
35 liquidity1: U256,
36}
37
38impl UniswapV2Pool {
39 pub fn new(address: Address) -> UniswapV2Pool {
40 UniswapV2Pool {
41 address,
42 token0: Address::ZERO,
43 token1: Address::ZERO,
44 factory: Address::ZERO,
45 protocol: PoolProtocol::UniswapV2Like,
46 fee: U256::from(9970),
47 encoder: UniswapV2PoolAbiEncoder {},
48 reserves_cell: None,
49 liquidity0: U256::ZERO,
50 liquidity1: U256::ZERO,
51 }
52 }
53
54 pub fn new_with_data(
55 address: Address,
56 token0: Address,
57 token1: Address,
58 factory: Address,
59 liquidity0: U256,
60 liquidity1: U256,
61 ) -> UniswapV2Pool {
62 UniswapV2Pool {
63 address,
64 token0,
65 token1,
66 factory,
67 protocol: PoolProtocol::UniswapV2Like,
68 fee: U256::from(9970),
69 encoder: UniswapV2PoolAbiEncoder {},
70 reserves_cell: None,
71 liquidity0,
72 liquidity1,
73 }
74 }
75
76 pub fn set_fee(self, fee: U256) -> Self {
77 Self { fee, ..self }
78 }
79
80 pub fn get_zero_for_one(token_address_from: Address, token_address_to: Address) -> bool {
81 token_address_from < token_address_to
82 }
83
84 fn get_uni2_protocol_by_factory(factory_address: Address) -> PoolProtocol {
85 if factory_address == FactoryAddress::UNISWAP_V2 {
86 PoolProtocol::UniswapV2
87 } else if factory_address == FactoryAddress::SUSHISWAP_V2 {
88 PoolProtocol::Sushiswap
89 } else if factory_address == FactoryAddress::NOMISWAP {
90 PoolProtocol::NomiswapStable
91 } else if factory_address == FactoryAddress::DOOARSWAP {
92 PoolProtocol::DooarSwap
93 } else if factory_address == FactoryAddress::SAFESWAP {
94 PoolProtocol::Safeswap
95 } else if factory_address == FactoryAddress::MINISWAP {
96 PoolProtocol::Miniswap
97 } else if factory_address == FactoryAddress::SHIBASWAP {
98 PoolProtocol::Shibaswap
99 } else if factory_address == FactoryAddress::OG_PEPE {
100 PoolProtocol::OgPepe
101 } else if factory_address == FactoryAddress::ANTFARM {
102 PoolProtocol::AntFarm
103 } else if factory_address == FactoryAddress::INTEGRAL {
104 PoolProtocol::Integral
105 } else {
106 PoolProtocol::UniswapV2Like
107 }
108 }
109
110 fn get_fee_by_protocol(protocol: PoolProtocol) -> U256 {
111 match protocol {
112 PoolProtocol::DooarSwap | PoolProtocol::OgPepe => U256::from(9900),
113 _ => U256::from(9970),
114 }
115 }
116
117 fn storage_to_reserves(value: U256) -> (U256, U256) {
118 ((value >> 0) & *U112_MASK, (value >> (112)) & *U112_MASK)
120 }
121
122 pub fn fetch_pool_data_evm<DB: DatabaseRef<Error = KabuDBError> + ?Sized>(db: &DB, address: Address) -> Result<Self> {
123 let token0 = UniswapV2EVMStateReader::token0(db, address)?;
124 let token1 = UniswapV2EVMStateReader::token1(db, address)?;
125 let factory = UniswapV2EVMStateReader::factory(db, address)?;
126 let protocol = Self::get_uni2_protocol_by_factory(factory);
127
128 let fee = Self::get_fee_by_protocol(protocol);
129
130 let ret = UniswapV2Pool {
131 address,
132 token0,
133 token1,
134 fee,
135 factory,
136 protocol,
137 encoder: UniswapV2PoolAbiEncoder {},
138 reserves_cell: None,
139 liquidity0: Default::default(),
140 liquidity1: Default::default(),
141 };
142 debug!("fetch_pool_data_evm {:?} {:?} {} {:?} {}", token0, token1, fee, factory, protocol);
143
144 Ok(ret)
145 }
146
147 pub async fn fetch_pool_data<N: Network, P: Provider<N> + Send + Sync + Clone + 'static>(client: P, address: Address) -> Result<Self> {
148 let uni2_pool = IUniswapV2Pair::IUniswapV2PairInstance::new(address, client.clone());
149
150 let token0: Address = uni2_pool.token0().call().await?;
151 let token1: Address = uni2_pool.token1().call().await?;
152 let factory: Address = uni2_pool.factory().call().await?;
153 let reserves = uni2_pool.getReserves().call().await?.clone();
154
155 let storage_reserves_cell = client.get_storage_at(address, U256::from(8)).block_id(BlockNumberOrTag::Latest.into()).await.unwrap();
156
157 let storage_reserves = Self::storage_to_reserves(storage_reserves_cell);
158
159 let reserves_cell: Option<U256> =
160 if storage_reserves.0 == U256::from(reserves.reserve0) && storage_reserves.1 == U256::from(reserves.reserve1) {
161 Some(U256::from(8))
162 } else {
163 debug!("{storage_reserves:?} {reserves:?}");
164 None
165 };
166
167 let protocol = UniswapV2Pool::get_uni2_protocol_by_factory(factory);
168
169 let fee = Self::get_fee_by_protocol(protocol);
170
171 let ret = UniswapV2Pool {
172 address,
173 token0,
174 token1,
175 factory,
176 protocol,
177 fee,
178 reserves_cell,
179 liquidity0: U256::from(reserves.reserve0),
180 liquidity1: U256::from(reserves.reserve1),
181 encoder: UniswapV2PoolAbiEncoder {},
182 };
183 Ok(ret)
184 }
185
186 pub fn fetch_reserves<DB: DatabaseRef<Error = KabuDBError> + ?Sized>(&self, db: &DB) -> Result<(U256, U256)> {
187 let (reserve_0, reserve_1) = UniswapV2EVMStateReader::get_reserves(db, self.address)?;
188 Ok((reserve_0, reserve_1))
189 }
190}
191
192impl Pool for UniswapV2Pool {
193 fn as_any<'a>(&self) -> &dyn Any {
194 self
195 }
196 fn get_class(&self) -> PoolClass {
197 PoolClass::UniswapV2
198 }
199
200 fn get_protocol(&self) -> PoolProtocol {
201 self.protocol
202 }
203
204 fn get_address(&self) -> PoolId {
205 PoolId::Address(self.address)
206 }
207 fn get_pool_id(&self) -> PoolId {
208 PoolId::Address(self.address)
209 }
210
211 fn get_fee(&self) -> U256 {
212 self.fee
213 }
214
215 fn get_tokens(&self) -> Vec<Address> {
216 vec![self.token0, self.token1]
217 }
218
219 fn get_swap_directions(&self) -> Vec<SwapDirection> {
220 vec![(self.token0, self.token1).into(), (self.token1, self.token0).into()]
221 }
222
223 fn calculate_out_amount(
224 &self,
225 db: &dyn DatabaseRef<Error = KabuDBError>,
226 token_address_from: &Address,
227 token_address_to: &Address,
228 in_amount: U256,
229 ) -> Result<(U256, u64), ErrReport> {
230 let (reserves_0, reserves_1) = self.fetch_reserves(db)?;
231
232 let (reserve_in, reserve_out) = match token_address_from < token_address_to {
233 true => (reserves_0, reserves_1),
234 false => (reserves_1, reserves_0),
235 };
236
237 let amount_in_with_fee = in_amount.checked_mul(self.fee).ok_or(eyre!("AMOUNT_IN_WITH_FEE_OVERFLOW"))?;
238 let numerator = amount_in_with_fee.checked_mul(reserve_out).ok_or(eyre!("NUMERATOR_OVERFLOW"))?;
239 let denominator = reserve_in.checked_mul(U256::from(10000)).ok_or(eyre!("DENOMINATOR_OVERFLOW"))?;
240 let denominator = denominator.checked_add(amount_in_with_fee).ok_or(eyre!("DENOMINATOR_OVERFLOW_FEE"))?;
241
242 let out_amount = numerator.checked_div(denominator).ok_or(eyre!("CANNOT_CALCULATE_ZERO_RESERVE"))?;
243 if out_amount > reserve_out {
244 Err(eyre!("RESERVE_EXCEEDED"))
245 } else if out_amount.is_zero() {
246 Err(eyre!("OUT_AMOUNT_IS_ZERO"))
247 } else {
248 Ok((out_amount, 100_000))
249 }
250 }
251
252 fn calculate_in_amount(
253 &self,
254 db: &dyn DatabaseRef<Error = KabuDBError>,
255 token_address_from: &Address,
256 token_address_to: &Address,
257 out_amount: U256,
258 ) -> Result<(U256, u64), ErrReport> {
259 let (reserves_0, reserves_1) = self.fetch_reserves(db)?;
260
261 let (reserve_in, reserve_out) = match token_address_from.lt(token_address_to) {
262 true => (reserves_0, reserves_1),
263 false => (reserves_1, reserves_0),
264 };
265
266 if out_amount > reserve_out {
267 return Err(eyre!("RESERVE_OUT_EXCEEDED"));
268 }
269 let numerator = reserve_in.checked_mul(out_amount).ok_or(eyre!("NUMERATOR_OVERFLOW"))?;
270 let numerator = numerator.checked_mul(U256::from(10000)).ok_or(eyre!("NUMERATOR_OVERFLOW_FEE"))?;
271 let denominator = reserve_out.checked_sub(out_amount).ok_or(eyre!("DENOMINATOR_UNDERFLOW"))?;
272 let denominator = denominator.checked_mul(self.fee).ok_or(eyre!("DENOMINATOR_OVERFLOW_FEE"))?;
273
274 if denominator.is_zero() {
275 Err(eyre!("CANNOT_CALCULATE_ZERO_RESERVE"))
276 } else {
277 let in_amount = numerator.div(denominator); if in_amount.is_zero() {
279 Err(eyre!("IN_AMOUNT_IS_ZERO"))
280 } else {
281 let in_amount = in_amount.checked_add(U256::ONE).ok_or(eyre!("IN_AMOUNT_OVERFLOW"))?;
282 Ok((in_amount, 100_000))
283 }
284 }
285 }
286
287 fn can_flash_swap(&self) -> bool {
288 true
289 }
290
291 fn can_calculate_in_amount(&self) -> bool {
292 true
293 }
294
295 fn get_abi_encoder(&self) -> Option<&dyn PoolAbiEncoder> {
296 Some(&self.encoder)
297 }
298
299 fn get_read_only_cell_vec(&self) -> Vec<U256> {
300 Vec::new()
301 }
302
303 fn get_state_required(&self) -> Result<RequiredState> {
304 let mut state_required = RequiredState::new();
305
306 let reserves_call_data_vec = IUniswapV2Pair::IUniswapV2PairCalls::getReserves(IUniswapV2Pair::getReservesCall {}).abi_encode();
307 let factory_call_data_vec = IUniswapV2Pair::IUniswapV2PairCalls::factory(IUniswapV2Pair::factoryCall {}).abi_encode();
308
309 state_required.add_call(self.address, reserves_call_data_vec).add_call(self.address, factory_call_data_vec).add_slot_range(
310 self.address,
311 U256::from(0),
312 0x20,
313 );
314
315 for token_address in self.get_tokens() {
316 state_required
317 .add_call(token_address, IERC20::IERC20Calls::balanceOf(IERC20::balanceOfCall { account: self.address }).abi_encode());
318 }
319
320 Ok(state_required)
321 }
322
323 fn is_native(&self) -> bool {
324 false
325 }
326
327 fn preswap_requirement(&self) -> PreswapRequirement {
328 PreswapRequirement::Transfer(self.address)
329 }
330}
331
332#[derive(Clone, Copy)]
333struct UniswapV2PoolAbiEncoder {}
334
335impl PoolAbiEncoder for UniswapV2PoolAbiEncoder {
336 fn encode_swap_out_amount_provided(
337 &self,
338 token_from_address: Address,
339 token_to_address: Address,
340 amount: U256,
341 recipient: Address,
342 payload: Bytes,
343 ) -> Result<Bytes> {
344 let swap_call = if token_from_address < token_to_address {
345 IUniswapV2Pair::swapCall { amount0Out: U256::ZERO, amount1Out: amount, to: recipient, data: payload }
346 } else {
347 IUniswapV2Pair::swapCall { amount0Out: amount, amount1Out: U256::ZERO, to: recipient, data: payload }
348 };
349
350 Ok(Bytes::from(IUniswapV2Pair::IUniswapV2PairCalls::swap(swap_call).abi_encode()))
351 }
352
353 fn swap_out_amount_offset(&self, token_from_address: Address, token_to_address: Address) -> Option<u32> {
354 if token_from_address < token_to_address {
355 Some(0x24)
356 } else {
357 Some(0x04)
358 }
359 }
360
361 fn swap_out_amount_return_offset(&self, token_from_address: Address, token_to_address: Address) -> Option<u32> {
362 if token_from_address < token_to_address {
363 Some(0x20)
364 } else {
365 Some(0x00)
366 }
367 }
368}
369
370#[cfg(test)]
372mod test {
373 use super::*;
374 use alloy::primitives::{address, BlockNumber};
375 use alloy::rpc::types::BlockId;
376 use kabu_defi_abi::uniswap2::IUniswapV2Router;
377 use kabu_defi_address_book::PeripheryAddress;
378 use kabu_evm_db::KabuDBType;
379 use kabu_node_debug_provider::{AnvilDebugProviderFactory, AnvilDebugProviderType};
380 use kabu_types_blockchain::KabuDataTypesEthereum;
381 use kabu_types_entities::required_state::RequiredStateReader;
382 use rand::Rng;
383 use std::env;
384
385 const POOL_ADDRESSES: [Address; 4] = [
386 address!("322BBA387c825180ebfB62bD8E6969EBe5b5e52d"), address!("b4e16d0168e52d35cacd2c6185b44281ec28c9dc"), address!("0d4a11d5eeaac28ec3f61d100daf4d40471f1852"), address!("ddd23787a6b80a794d952f5fb036d0b31a8e6aff"), ];
391
392 #[tokio::test]
393 async fn test_fetch_reserves() -> Result<()> {
394 let _ = env_logger::try_init_from_env(env_logger::Env::default().default_filter_or(
395 "info,kabu_types_entities::required_state=trace,kabu_types_blockchain::state_update=off,alloy_rpc_client::call=off,tungstenite=off",
396 ));
397
398 let block_number = 20935488u64;
399
400 dotenvy::from_filename(".env.test").ok();
401 let node_url = env::var("MAINNET_WS")?;
402 let client = AnvilDebugProviderFactory::from_node_on_block(node_url, BlockNumber::from(block_number)).await?;
403
404 for pool_address in POOL_ADDRESSES {
405 let pool_contract = IUniswapV2Pair::new(pool_address, client.clone());
406 let contract_reserves = pool_contract.getReserves().call().block(BlockId::from(block_number)).await?;
407 let reserves_0_original = U256::from(contract_reserves.reserve0);
408 let reserves_1_original = U256::from(contract_reserves.reserve1);
409
410 let pool = UniswapV2Pool::fetch_pool_data(client.clone(), pool_address).await?;
411 let state_required = pool.get_state_required()?;
412 let state_update =
413 RequiredStateReader::<KabuDataTypesEthereum>::fetch_calls_and_slots(client.clone(), state_required, Some(block_number))
414 .await?;
415
416 let mut state_db = KabuDBType::default();
417 state_db.apply_geth_update(state_update);
418
419 let (reserves_0, reserves_1) = pool.fetch_reserves(&state_db)?;
421
422 assert_eq!(reserves_0, reserves_0_original, "{}", format!("Missmatch for pool={:?}", pool_address));
423 assert_eq!(reserves_1, reserves_1_original, "{}", format!("Missmatch for pool={:?}", pool_address));
424 }
425 Ok(())
426 }
427
428 async fn fetch_original_contract_amounts(
429 client: AnvilDebugProviderType,
430 pool_address: Address,
431 amount: U256,
432 block_number: u64,
433 amount_out: bool,
434 ) -> Result<U256> {
435 let router_contract = IUniswapV2Router::new(PeripheryAddress::UNISWAP_V2_ROUTER, client.clone());
436
437 let pool_contract = IUniswapV2Pair::new(pool_address, client.clone());
439 let contract_reserves = pool_contract.getReserves().call().block(BlockId::from(block_number)).await?;
440
441 let token0 = pool_contract.token0().call().await?;
442 let token1 = pool_contract.token1().call().await?;
443
444 let (reserve_in, reserve_out) = match token0 < token1 {
445 true => (U256::from(contract_reserves.reserve0), U256::from(contract_reserves.reserve1)),
446 false => (U256::from(contract_reserves.reserve1), U256::from(contract_reserves.reserve0)),
447 };
448
449 if amount_out {
450 let contract_amount_out =
451 router_contract.getAmountOut(amount, reserve_in, reserve_out).call().block(BlockId::from(block_number)).await?;
452 Ok(contract_amount_out)
453 } else {
454 let contract_amount_in =
455 router_contract.getAmountIn(amount, reserve_in, reserve_out).call().block(BlockId::from(block_number)).await?;
456 Ok(contract_amount_in)
457 }
458 }
459
460 #[tokio::test]
461 async fn test_calculate_out_amount() -> Result<()> {
462 let block_number = 20935488u64;
464
465 dotenvy::from_filename(".env.test").ok();
466 let node_url = env::var("MAINNET_WS")?;
467 let client = AnvilDebugProviderFactory::from_node_on_block(node_url, BlockNumber::from(block_number)).await?;
468
469 let amount_in = U256::from(133_333_333_333u128) + U256::from(rand::rng().random_range(0..100_000_000_000u64));
470 for pool_address in POOL_ADDRESSES {
471 let pool = UniswapV2Pool::fetch_pool_data(client.clone(), pool_address).await?;
472 let state_required = pool.get_state_required()?;
473 let state_update =
474 RequiredStateReader::<KabuDataTypesEthereum>::fetch_calls_and_slots(client.clone(), state_required, Some(block_number))
475 .await?;
476
477 let mut state_db = KabuDBType::default();
478 state_db.apply_geth_update(state_update);
479
480 let contract_amount_out = fetch_original_contract_amounts(client.clone(), pool_address, amount_in, block_number, true).await?;
482
483 let (amount_out, gas_used) = pool.calculate_out_amount(&state_db, &pool.token0, &pool.token1, amount_in)?;
484
485 assert_eq!(amount_out, contract_amount_out, "{}", format!("Missmatch for pool={:?}, amount_in={}", pool_address, amount_in));
486 assert_eq!(gas_used, 100_000);
487 }
488 Ok(())
489 }
490
491 #[tokio::test]
492 async fn test_calculate_in_amount() -> Result<()> {
493 let block_number = 20935488u64;
495
496 dotenvy::from_filename(".env.test").ok();
497 let node_url = env::var("MAINNET_WS")?;
498 let client = AnvilDebugProviderFactory::from_node_on_block(node_url, BlockNumber::from(block_number)).await?;
499
500 let amount_out = U256::from(133_333_333_333u128) + U256::from(rand::rng().random_range(0..100_000_000_000u64));
501 for pool_address in POOL_ADDRESSES {
502 let pool = UniswapV2Pool::fetch_pool_data(client.clone(), pool_address).await?;
503 let state_required = pool.get_state_required()?;
504 let state_update =
505 RequiredStateReader::<KabuDataTypesEthereum>::fetch_calls_and_slots(client.clone(), state_required, Some(block_number))
506 .await?;
507
508 let mut state_db = KabuDBType::default();
509 state_db.apply_geth_update(state_update);
510
511 let contract_amount_in = fetch_original_contract_amounts(client.clone(), pool_address, amount_out, block_number, false).await?;
513
514 let (amount_in, gas_used) = pool.calculate_in_amount(&state_db, &pool.token0, &pool.token1, amount_out)?;
516
517 assert_eq!(amount_in, contract_amount_in, "{}", format!("Missmatch for pool={:?}, amount_out={}", pool_address, amount_out));
518 assert_eq!(gas_used, 100_000);
519 }
520 Ok(())
521 }
522}