kabu_defi_pools/
maverickpool.rs

1use crate::state_readers::UniswapV3EvmStateReader;
2use alloy::primitives::{Address, Bytes, U128, U256};
3use alloy::providers::{Network, Provider};
4use alloy::sol_types::{SolCall, SolInterface};
5use alloy_evm::EvmEnv;
6use eyre::Result;
7use kabu_defi_abi::IERC20;
8use kabu_defi_abi::maverick::IMaverickPool::{IMaverickPoolCalls, IMaverickPoolInstance, getStateCall};
9use kabu_defi_abi::maverick::IMaverickQuoter::{IMaverickQuoterCalls, calculateSwapCall};
10use kabu_defi_abi::maverick::{IMaverickPool, IMaverickQuoter, State};
11use kabu_defi_address_book::PeripheryAddress;
12use kabu_evm_db::KabuDBError;
13use kabu_evm_utils::evm_call;
14use kabu_types_market::{
15    MaverickError, Pool, PoolAbiEncoder, PoolClass, PoolError, PoolId, PoolProtocol, PreswapRequirement, RequiredState, SwapDirection,
16};
17use lazy_static::lazy_static;
18use revm::DatabaseRef;
19use std::any::Any;
20use tracing::error;
21
22lazy_static! {
23    static ref U256_ONE: U256 = U256::from(1);
24}
25
26#[allow(dead_code)]
27#[derive(Clone)]
28pub struct MaverickPool {
29    //contract_storage : ContractStorage,
30    address: Address,
31    pub token0: Address,
32    pub token1: Address,
33    liquidity0: U256,
34    liquidity1: U256,
35    fee: U256,
36    spacing: u32,
37    slot0: Option<State>,
38    factory: Address,
39    protocol: PoolProtocol,
40    encoder: MaverickAbiSwapEncoder,
41}
42
43impl MaverickPool {
44    pub fn new(address: Address) -> Self {
45        MaverickPool {
46            address,
47            token0: Address::ZERO,
48            token1: Address::ZERO,
49            liquidity0: U256::ZERO,
50            liquidity1: U256::ZERO,
51            fee: U256::ZERO,
52            spacing: 0,
53            slot0: None,
54            factory: Address::ZERO,
55            protocol: PoolProtocol::Maverick,
56            encoder: MaverickAbiSwapEncoder::new(address),
57        }
58    }
59
60    pub fn get_tick_bitmap_index(tick: i32, spacing: u32) -> i32 {
61        let tick_bitmap_index = tick / (spacing as i32);
62
63        if tick_bitmap_index < 0 { ((tick_bitmap_index + 1) / 256) - 1 } else { tick_bitmap_index >> 8 }
64    }
65
66    pub fn get_price_limit(token_address_from: &Address, token_address_to: &Address) -> U256 {
67        if *token_address_from < *token_address_to {
68            U256::from(4295128740u64)
69        } else {
70            U256::from_str_radix("1461446703485210103287273052203988822378723970341", 10).unwrap()
71        }
72    }
73
74    pub fn get_zero_for_one<T: Ord>(token_address_from: &T, token_address_to: &T) -> bool {
75        token_address_from.lt(token_address_to)
76    }
77
78    fn get_protocol_by_factory(_factory_address: Address) -> PoolProtocol {
79        PoolProtocol::Maverick
80    }
81
82    pub async fn fetch_pool_data<N: Network, P: Provider<N> + Send + Sync + Clone + 'static>(client: P, address: Address) -> Result<Self> {
83        let pool = IMaverickPoolInstance::new(address, client.clone());
84
85        let token0: Address = pool.tokenA().call().await?;
86        let token1: Address = pool.tokenB().call().await?;
87        let fee: U256 = pool.fee().call().await?;
88        let slot0 = pool.getState().call().await?;
89        let factory: Address = pool.factory().call().await?;
90        let spacing: u32 = pool.tickSpacing().call().await?.to();
91
92        let token0_erc20 = IERC20::IERC20Instance::new(token0, client.clone());
93        let token1_erc20 = IERC20::IERC20Instance::new(token1, client.clone());
94
95        let liquidity0: U256 = token0_erc20.balanceOf(address).call().await?;
96        let liquidity1: U256 = token1_erc20.balanceOf(address).call().await?;
97
98        let protocol = MaverickPool::get_protocol_by_factory(factory);
99
100        let ret = MaverickPool {
101            address,
102            token0,
103            token1,
104            fee,
105            slot0: Some(slot0),
106            liquidity0,
107            liquidity1,
108            factory,
109            protocol,
110            spacing,
111            encoder: MaverickAbiSwapEncoder { pool_address: address },
112        };
113
114        Ok(ret)
115    }
116    pub fn fetch_pool_data_evm<DB: DatabaseRef<Error = KabuDBError> + ?Sized>(db: &DB, evm_env: &EvmEnv, address: Address) -> Result<Self> {
117        let token0: Address = UniswapV3EvmStateReader::token0(db, evm_env, address)?;
118        let token1: Address = UniswapV3EvmStateReader::token1(db, evm_env, address)?;
119        let fee = UniswapV3EvmStateReader::fee(db, evm_env, address)?;
120        let factory: Address = UniswapV3EvmStateReader::factory(db, evm_env, address)?;
121        let spacing: u32 = UniswapV3EvmStateReader::tick_spacing(db, evm_env, address)?;
122
123        let protocol = Self::get_protocol_by_factory(factory);
124
125        let ret = MaverickPool {
126            address,
127            token0,
128            token1,
129            liquidity0: Default::default(),
130            liquidity1: Default::default(),
131            fee: U256::from(fee),
132            spacing,
133            slot0: None,
134            factory,
135            protocol,
136            encoder: MaverickAbiSwapEncoder { pool_address: address },
137        };
138
139        Ok(ret)
140    }
141}
142
143impl Pool for MaverickPool {
144    fn as_any<'a>(&self) -> &dyn Any {
145        self
146    }
147
148    fn get_class(&self) -> PoolClass {
149        PoolClass::Maverick
150    }
151
152    fn get_protocol(&self) -> PoolProtocol {
153        self.protocol
154    }
155
156    fn get_address(&self) -> PoolId {
157        PoolId::Address(self.address)
158    }
159
160    fn get_pool_id(&self) -> PoolId {
161        PoolId::Address(self.address)
162    }
163
164    fn get_fee(&self) -> U256 {
165        self.fee
166    }
167
168    fn get_tokens(&self) -> Vec<Address> {
169        vec![self.token0, self.token1]
170    }
171
172    fn get_swap_directions(&self) -> Vec<SwapDirection> {
173        vec![(self.token0, self.token1).into(), (self.token1, self.token0).into()]
174    }
175
176    fn calculate_out_amount(
177        &self,
178        db: &dyn DatabaseRef<Error = KabuDBError>,
179        evm_env: &EvmEnv,
180        token_address_from: &Address,
181        token_address_to: &Address,
182        in_amount: U256,
183    ) -> Result<(U256, u64), PoolError> {
184        if in_amount >= U256::from(U128::MAX) {
185            error!("IN_AMOUNT_EXCEEDS_MAX {}", self.address.to_checksum(None));
186            return Err(MaverickError::MaxInAmount.into());
187        }
188
189        let token_a_in = MaverickPool::get_zero_for_one(token_address_from, token_address_to);
190        //let sqrt_price_limit = MaverickPool::get_price_limit(token_address_from, token_address_to);
191
192        let call_data_vec = IMaverickQuoterCalls::calculateSwap(calculateSwapCall {
193            pool: self.address,
194            amount: in_amount.to(),
195            tokenAIn: token_a_in,
196            exactOutput: false,
197            sqrtPriceLimit: U256::ZERO,
198        })
199        .abi_encode();
200
201        let (value, gas_used, _) = evm_call(db, evm_env.clone(), PeripheryAddress::MAVERICK_QUOTER, call_data_vec)?;
202
203        let ret = calculateSwapCall::abi_decode_returns(&value)
204            .map_err(|e| PoolError::AbiDecodingError { method: "calculateSwap", source: e })?;
205
206        if ret.is_zero() {
207            Err(MaverickError::OutAmountIsZero.into())
208        } else {
209            Ok((ret.checked_sub(*U256_ONE).ok_or(PoolError::InvalidInput { reason: "subtraction overflow" })?, gas_used))
210        }
211    }
212
213    fn calculate_in_amount(
214        &self,
215        db: &dyn DatabaseRef<Error = KabuDBError>,
216        evm_env: &EvmEnv,
217        token_address_from: &Address,
218        token_address_to: &Address,
219        out_amount: U256,
220    ) -> Result<(U256, u64), PoolError> {
221        if out_amount >= U256::from(U128::MAX) {
222            error!("OUT_AMOUNT_EXCEEDS_MAX {} ", self.get_address());
223            return Err(MaverickError::ReserveOutExceeded.into());
224        }
225
226        let token_a_in = MaverickPool::get_zero_for_one(token_address_from, token_address_to);
227        //let sqrt_price_limit = MaverickPool::get_price_limit(token_address_from, token_address_to);
228
229        let call_data_vec = IMaverickQuoterCalls::calculateSwap(calculateSwapCall {
230            pool: self.address,
231            amount: out_amount.to(),
232            tokenAIn: token_a_in,
233            exactOutput: true,
234            sqrtPriceLimit: U256::ZERO,
235        })
236        .abi_encode();
237
238        let (value, gas_used, _) = evm_call(db, evm_env.clone(), PeripheryAddress::MAVERICK_QUOTER, call_data_vec)?;
239
240        let ret = calculateSwapCall::abi_decode_returns(&value)
241            .map_err(|e| PoolError::AbiDecodingError { method: "calculateSwap", source: e })?;
242
243        if ret.is_zero() {
244            Err(MaverickError::InAmountIsZero.into())
245        } else {
246            Ok((ret.checked_add(*U256_ONE).ok_or(PoolError::InvalidInput { reason: "addition overflow" })?, gas_used))
247        }
248    }
249
250    fn can_flash_swap(&self) -> bool {
251        true
252    }
253
254    fn can_calculate_in_amount(&self) -> bool {
255        true
256    }
257
258    fn get_abi_encoder(&self) -> Option<&dyn PoolAbiEncoder> {
259        Some(&self.encoder)
260    }
261
262    fn get_read_only_cell_vec(&self) -> Vec<U256> {
263        Vec::new()
264    }
265
266    fn get_state_required(&self) -> Result<RequiredState> {
267        let tick = self.slot0.clone().unwrap().activeTick;
268
269        let quoter_swap_0_1_call = IMaverickQuoterCalls::calculateSwap(calculateSwapCall {
270            pool: self.address,
271            amount: (self.liquidity0 / U256::from(100)).to(),
272            tokenAIn: true,
273            exactOutput: false,
274            sqrtPriceLimit: U256::ZERO,
275        })
276        .abi_encode();
277
278        //let sqrt_price_limit = MaverickPool::get_price_limit(&self.token1, &self.token0);
279
280        let quoter_swap_1_0_call = IMaverickQuoterCalls::calculateSwap(calculateSwapCall {
281            pool: self.address,
282            amount: (self.liquidity1 / U256::from(100)).to(),
283            tokenAIn: false,
284            exactOutput: false,
285            sqrtPriceLimit: U256::ZERO,
286        })
287        .abi_encode();
288
289        //let tick_bitmap_index = MaverickPool::get_tick_bitmap_index(tick, self.spacing.as_u32());
290        let tick_bitmap_index = tick;
291
292        let pool_address = self.address;
293
294        let mut state_required = RequiredState::new();
295        state_required
296            .add_call(self.address, IMaverickPoolCalls::getState(getStateCall {}).abi_encode())
297            .add_call(
298                PeripheryAddress::MAVERICK_QUOTER,
299                IMaverickQuoterCalls::getBinsAtTick(IMaverickQuoter::getBinsAtTickCall { pool: pool_address, tick: tick_bitmap_index - 4 })
300                    .abi_encode(),
301            )
302            .add_call(
303                PeripheryAddress::MAVERICK_QUOTER,
304                IMaverickQuoterCalls::getBinsAtTick(IMaverickQuoter::getBinsAtTickCall { pool: pool_address, tick: tick_bitmap_index - 3 })
305                    .abi_encode(),
306            )
307            .add_call(
308                PeripheryAddress::MAVERICK_QUOTER,
309                IMaverickQuoterCalls::getBinsAtTick(IMaverickQuoter::getBinsAtTickCall { pool: pool_address, tick: tick_bitmap_index - 2 })
310                    .abi_encode(),
311            )
312            .add_call(
313                PeripheryAddress::MAVERICK_QUOTER,
314                IMaverickQuoterCalls::getBinsAtTick(IMaverickQuoter::getBinsAtTickCall { pool: pool_address, tick: tick_bitmap_index - 1 })
315                    .abi_encode(),
316            )
317            .add_call(
318                PeripheryAddress::MAVERICK_QUOTER,
319                IMaverickQuoterCalls::getBinsAtTick(IMaverickQuoter::getBinsAtTickCall { pool: pool_address, tick: tick_bitmap_index })
320                    .abi_encode(),
321            )
322            .add_call(
323                PeripheryAddress::MAVERICK_QUOTER,
324                IMaverickQuoterCalls::getBinsAtTick(IMaverickQuoter::getBinsAtTickCall { pool: pool_address, tick: tick_bitmap_index + 1 })
325                    .abi_encode(),
326            )
327            .add_call(
328                PeripheryAddress::MAVERICK_QUOTER,
329                IMaverickQuoterCalls::getBinsAtTick(IMaverickQuoter::getBinsAtTickCall { pool: pool_address, tick: tick_bitmap_index + 2 })
330                    .abi_encode(),
331            )
332            .add_call(
333                PeripheryAddress::MAVERICK_QUOTER,
334                IMaverickQuoterCalls::getBinsAtTick(IMaverickQuoter::getBinsAtTickCall { pool: pool_address, tick: tick_bitmap_index + 3 })
335                    .abi_encode(),
336            )
337            .add_call(
338                PeripheryAddress::MAVERICK_QUOTER,
339                IMaverickQuoterCalls::getBinsAtTick(IMaverickQuoter::getBinsAtTickCall { pool: pool_address, tick: tick_bitmap_index + 4 })
340                    .abi_encode(),
341            )
342            .add_call(PeripheryAddress::MAVERICK_QUOTER, quoter_swap_0_1_call)
343            .add_call(PeripheryAddress::MAVERICK_QUOTER, quoter_swap_1_0_call)
344            .add_slot_range(self.address, U256::from(0), 0x20);
345
346        for token_address in self.get_tokens() {
347            state_required.add_call(token_address, IERC20::balanceOfCall { account: pool_address }.abi_encode());
348        }
349
350        Ok(state_required)
351    }
352
353    fn is_native(&self) -> bool {
354        false
355    }
356
357    fn preswap_requirement(&self) -> PreswapRequirement {
358        PreswapRequirement::Callback
359    }
360}
361
362#[allow(dead_code)]
363#[derive(Clone, Copy)]
364struct MaverickAbiSwapEncoder {
365    pool_address: Address,
366}
367
368impl MaverickAbiSwapEncoder {
369    pub fn new(pool_address: Address) -> Self {
370        Self { pool_address }
371    }
372}
373
374impl PoolAbiEncoder for MaverickAbiSwapEncoder {
375    fn encode_swap_in_amount_provided(
376        &self,
377        token_from_address: Address,
378        token_to_address: Address,
379        amount: U256,
380        recipient: Address,
381        payload: Bytes,
382    ) -> Result<Bytes> {
383        //let sqrt_price_limit_x96 = MaverickPool::get_price_limit(&token_from_address, &token_to_address);
384
385        let token_a_in = MaverickPool::get_zero_for_one(&token_from_address, &token_to_address);
386
387        let swap_call = IMaverickPool::swapCall {
388            recipient,
389            amount,
390            tokenAIn: token_a_in,
391            exactOutput: false,
392            sqrtPriceLimit: U256::ZERO,
393            data: payload,
394        };
395
396        Ok(Bytes::from(IMaverickPoolCalls::swap(swap_call).abi_encode()))
397    }
398
399    fn encode_swap_out_amount_provided(
400        &self,
401        token_from_address: Address,
402        token_to_address: Address,
403        amount: U256,
404        recipient: Address,
405        payload: Bytes,
406    ) -> Result<Bytes> {
407        let token_a_in = MaverickPool::get_zero_for_one(&token_from_address, &token_to_address);
408        let sqrt_price_limit_x96 = MaverickPool::get_price_limit(&token_from_address, &token_to_address);
409
410        let swap_call = IMaverickPool::swapCall {
411            recipient,
412            amount,
413            tokenAIn: token_a_in,
414            exactOutput: true,
415            sqrtPriceLimit: sqrt_price_limit_x96,
416            data: payload,
417        };
418
419        Ok(Bytes::from(IMaverickPoolCalls::swap(swap_call).abi_encode()))
420    }
421
422    fn swap_in_amount_offset(&self, _token_from_address: Address, _token_to_address: Address) -> Option<u32> {
423        Some(0x24)
424    }
425    fn swap_out_amount_offset(&self, _token_from_address: Address, _token_to_address: Address) -> Option<u32> {
426        Some(0x24)
427    }
428    fn swap_out_amount_return_offset(&self, _token_from_address: Address, _token_to_address: Address) -> Option<u32> {
429        Some(0x20)
430    }
431    fn swap_in_amount_return_offset(&self, _token_from_address: Address, _token_to_address: Address) -> Option<u32> {
432        Some(0x20)
433    }
434}
435
436#[cfg(test)]
437mod tests {
438    use super::*;
439    use alloy::rpc::types::BlockNumberOrTag;
440    use kabu_defi_abi::maverick::IMaverickQuoter::IMaverickQuoterInstance;
441    use kabu_evm_db::KabuDBType;
442    use kabu_node_debug_provider::AnvilDebugProviderFactory;
443    use kabu_types_market::RequiredStateReader;
444    use revm::database::CacheDB;
445    use std::env;
446    use tracing::debug;
447
448    #[ignore]
449    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
450    async fn test_pool() -> Result<()> {
451        let _ = env_logger::try_init_from_env(env_logger::Env::default().default_filter_or("info,defi_pools=off"));
452
453        dotenvy::from_filename(".env.test").ok();
454        let node_url = env::var("MAINNET_WS")?;
455
456        let client = AnvilDebugProviderFactory::from_node_on_block(node_url, 20045799).await?;
457
458        let pool_address: Address = "0x352B186090068Eb35d532428676cE510E17AB581".parse().unwrap();
459
460        let pool = MaverickPool::fetch_pool_data(client.clone(), pool_address).await.unwrap();
461
462        let state_required = pool.get_state_required()?;
463
464        let state_required = RequiredStateReader::fetch_calls_and_slots(client.clone(), state_required, Some(20045799)).await?;
465        debug!("{:?}", state_required);
466
467        let block_number = 20045799u64;
468
469        use kabu_types_market::MarketState;
470        let mut market_state = MarketState::new(KabuDBType::default());
471        market_state.state_db.apply_geth_update(state_required);
472        let _block = client.get_block_by_number(BlockNumberOrTag::Number(block_number)).await?.unwrap();
473        let cache_db = CacheDB::new(market_state.state_db.clone());
474
475        let amount = U256::from(pool.liquidity1 / U256::from(1000));
476
477        let quoter = IMaverickQuoterInstance::new(PeripheryAddress::MAVERICK_QUOTER, client.clone());
478
479        let resp = quoter.calculateSwap(pool_address, amount.to(), false, false, U256::ZERO).call().await?;
480        debug!("Router call : {:?}", resp);
481        assert_ne!(resp, U256::ZERO);
482
483        let (out_amount, gas_used) = pool
484            .calculate_out_amount(&cache_db, &EvmEnv::default(), &pool.token1, &pool.token0, U256::from(pool.liquidity1 / U256::from(1000)))
485            .unwrap();
486        debug!("{} {} {}", pool.get_protocol(), out_amount, gas_used);
487        assert_ne!(out_amount, U256::ZERO);
488        assert!(gas_used > 100000);
489
490        let (out_amount, gas_used) = pool
491            .calculate_out_amount(&cache_db, &EvmEnv::default(), &pool.token0, &pool.token1, U256::from(pool.liquidity0 / U256::from(1000)))
492            .unwrap();
493        debug!("{} {} {}", pool.get_protocol(), out_amount, gas_used);
494        assert_ne!(out_amount, U256::ZERO);
495        assert!(gas_used > 100000);
496
497        Ok(())
498    }
499}