kabu_defi_pools/db_reader/
uniswapv3.rs

1use std::ops::{Shl, Shr};
2
3use alloy::primitives::{Address, Signed, Uint, B256, I256};
4use alloy::primitives::{U160, U256};
5use eyre::Result;
6use lazy_static::lazy_static;
7use revm::DatabaseRef;
8use tracing::trace;
9
10use kabu_defi_abi::uniswap3::IUniswapV3Pool::slot0Return;
11use kabu_evm_db::KabuDBType;
12use kabu_evm_utils::remv_db_direct_access::{try_read_cell, try_read_hashmap_cell};
13
14pub struct UniswapV3DbReader {}
15
16lazy_static! {
17    static ref BITS160MASK: U256 = U256::from(1).shl(160) - U256::from(1);
18    static ref BITS128MASK: U256 = U256::from(1).shl(128) - U256::from(1);
19    static ref BITS24MASK: U256 = U256::from(1).shl(24) - U256::from(1);
20    static ref BITS16MASK: U256 = U256::from(1).shl(16) - U256::from(1);
21    static ref BITS8MASK: U256 = U256::from(1).shl(8) - U256::from(1);
22    static ref BITS1MASK: U256 = U256::from(1);
23}
24impl UniswapV3DbReader {
25    pub fn fee_growth_global0_x128(db: &KabuDBType, address: Address) -> Result<U256> {
26        let cell = try_read_cell(db, &address, &U256::from(1))?;
27        Ok(cell)
28    }
29
30    pub fn fee_growth_global1_x128(db: &KabuDBType, address: Address) -> Result<U256> {
31        let cell = try_read_cell(db, &address, &U256::from(2))?;
32        Ok(cell)
33    }
34
35    pub fn protocol_fees(db: &KabuDBType, address: Address) -> Result<U256> {
36        let cell = try_read_cell(db, &address, &U256::from(3))?;
37        Ok(cell)
38    }
39
40    pub fn liquidity<DB: DatabaseRef>(db: &DB, address: Address) -> Result<u128> {
41        let cell = try_read_cell(&db, &address, &U256::from(4))?;
42        let cell: u128 = cell.saturating_to();
43        Ok(cell)
44    }
45
46    pub fn ticks_liquidity_net<DB: DatabaseRef>(db: &DB, address: Address, tick: i32) -> Result<i128> {
47        //i24
48        let cell = try_read_hashmap_cell(&db, &address, &U256::from(5), &U256::from_be_bytes(I256::try_from(tick)?.to_be_bytes::<32>()))?;
49        let unsigned_liqudity: Uint<128, 2> = cell.shr(U256::from(128)).to();
50        let signed_liquidity: Signed<128, 2> = Signed::<128, 2>::from_raw(unsigned_liqudity);
51        let lu128: u128 = unsigned_liqudity.to();
52        let li128: i128 = lu128 as i128;
53        trace!("ticks_liquidity_net {address} {tick} {cell} -> {signed_liquidity}");
54
55        Ok(li128)
56    }
57    pub fn tick_bitmap<DB: DatabaseRef>(db: &DB, address: Address, tick: i16) -> Result<U256> {
58        //i16
59        let cell = try_read_hashmap_cell(&db, &address, &U256::from(6), &U256::from_be_bytes(I256::try_from(tick)?.to_be_bytes::<32>()))?;
60        trace!("tickBitmap {address} {tick} {cell}");
61        Ok(cell)
62    }
63
64    pub fn position_info<DB: DatabaseRef>(db: &DB, address: Address, position: B256) -> Result<U256> {
65        //i16
66        let position: U256 = position.into();
67        let cell = try_read_hashmap_cell(db, &address, &U256::from(7), &position)?;
68        Ok(cell)
69    }
70
71    pub fn observations<DB: DatabaseRef>(db: &DB, address: Address, idx: u32) -> Result<U256> {
72        //i16
73        let cell = try_read_hashmap_cell(&db, &address, &U256::from(7), &U256::from(idx))?;
74        Ok(cell)
75    }
76
77    pub fn slot0<DB: DatabaseRef>(db: &DB, address: Address) -> Result<slot0Return> {
78        let cell = try_read_cell(&db, &address, &U256::from(0))?;
79        let tick: Uint<24, 1> = ((Shr::<U256>::shr(cell, U256::from(160))) & *BITS24MASK).to();
80        let tick: Signed<24, 1> = Signed::<24, 1>::from_raw(tick);
81        let tick: i32 = tick.as_i32();
82
83        let sqrt_price_x96: U160 = cell.bitand(*BITS160MASK).to();
84
85        Ok(slot0Return {
86            sqrtPriceX96: sqrt_price_x96,
87            tick: tick.try_into()?,
88            observationIndex: ((Shr::<U256>::shr(cell, U256::from(160 + 24))) & *BITS16MASK).to(),
89            observationCardinality: ((Shr::<U256>::shr(cell, U256::from(160 + 24 + 16))) & *BITS16MASK).to(),
90            observationCardinalityNext: ((Shr::<U256>::shr(cell, U256::from(160 + 24 + 16 + 16))) & *BITS16MASK).to(),
91            feeProtocol: ((Shr::<U256>::shr(cell, U256::from(160 + 24 + 16 + 16 + 16))) & *BITS8MASK).to(),
92            unlocked: ((Shr::<U256>::shr(cell, U256::from(160 + 24 + 16 + 16 + 16 + 8))) & *BITS1MASK).to(),
93        })
94    }
95}
96
97#[cfg(test)]
98mod test {
99    use alloy::primitives::Address;
100    use eyre::Result;
101    use revm::database::CacheDB;
102    use std::env;
103    use tracing::debug;
104
105    use kabu_defi_address_book::UniswapV3PoolAddress;
106    use kabu_evm_db::KabuDBType;
107    use kabu_node_debug_provider::AnvilDebugProviderFactory;
108    use kabu_types_blockchain::KabuDataTypesEthereum;
109    use kabu_types_entities::required_state::RequiredStateReader;
110    use kabu_types_entities::{MarketState, Pool};
111
112    use crate::db_reader::UniswapV3DbReader;
113    use crate::state_readers::UniswapV3EvmStateReader;
114    use crate::UniswapV3Pool;
115
116    #[ignore]
117    #[tokio::test]
118    async fn test_reader() -> Result<()> {
119        let _ = env_logger::try_init_from_env(env_logger::Env::default().default_filter_or(
120            "info,kabu_types_entities::required_state=off,kabu_types_blockchain::state_update=off,alloy_rpc_client::call=off,tungstenite=off",
121        ));
122
123        dotenvy::from_filename(".env.test").ok();
124        let node_url = env::var("MAINNET_WS")?;
125
126        let client = AnvilDebugProviderFactory::from_node_on_block(node_url, 20038285).await?;
127
128        let mut market_state = MarketState::new(KabuDBType::default());
129
130        let pool_address: Address = UniswapV3PoolAddress::USDC_WETH_500;
131
132        let pool = UniswapV3Pool::fetch_pool_data(client.clone(), pool_address).await?;
133
134        let state_required = pool.get_state_required()?;
135
136        let state_required =
137            RequiredStateReader::<KabuDataTypesEthereum>::fetch_calls_and_slots(client.clone(), state_required, None).await?;
138
139        market_state.state_db.apply_geth_update(state_required);
140
141        let cache_db = CacheDB::new(market_state.state_db.clone());
142
143        let factory_evm = UniswapV3EvmStateReader::factory(&cache_db, pool_address)?;
144        let token0_evm = UniswapV3EvmStateReader::token0(&cache_db, pool_address)?;
145        let token1_evm = UniswapV3EvmStateReader::token1(&cache_db, pool_address)?;
146        debug!("{factory_evm:?} {token0_evm:?} {token1_evm:?}");
147
148        let slot0_evm = UniswapV3EvmStateReader::slot0(&cache_db, pool_address)?;
149
150        let slot0_db = UniswapV3DbReader::slot0(&market_state.state_db, pool_address)?;
151
152        debug!("evm : {slot0_evm:?}");
153        debug!("db  : {slot0_db:?}");
154
155        assert_eq!(slot0_evm, slot0_db);
156
157        Ok(())
158    }
159}