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