loom_defi_pools/db_reader/
uniswapv3.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
use std::ops::{BitAnd, Shl, Shr};

use alloy_primitives::{Address, Signed, Uint, B256, I256};
use alloy_primitives::{U160, U256};
use eyre::Result;
use lazy_static::lazy_static;
use revm::DatabaseRef;
use tracing::trace;

use loom_defi_abi::uniswap3::IUniswapV3Pool::slot0Return;
use loom_evm_db::LoomDBType;
use loom_evm_utils::remv_db_direct_access::{try_read_cell, try_read_hashmap_cell};

pub struct UniswapV3DBReader {}

lazy_static! {
    static ref BITS160MASK: U256 = U256::from(1).shl(160) - U256::from(1);
    static ref BITS128MASK: U256 = U256::from(1).shl(128) - U256::from(1);
    static ref BITS24MASK: U256 = U256::from(1).shl(24) - U256::from(1);
    static ref BITS16MASK: U256 = U256::from(1).shl(16) - U256::from(1);
    static ref BITS8MASK: U256 = U256::from(1).shl(8) - U256::from(1);
    static ref BITS1MASK: U256 = U256::from(1);
}
impl UniswapV3DBReader {
    pub fn fee_growth_global0_x128(db: &LoomDBType, address: Address) -> Result<U256> {
        let cell = try_read_cell(db, &address, &U256::from(1))?;
        Ok(cell)
    }

    pub fn fee_growth_global1_x128(db: &LoomDBType, address: Address) -> Result<U256> {
        let cell = try_read_cell(db, &address, &U256::from(2))?;
        Ok(cell)
    }

    pub fn protocol_fees(db: &LoomDBType, address: Address) -> Result<U256> {
        let cell = try_read_cell(db, &address, &U256::from(3))?;
        Ok(cell)
    }

    pub fn liquidity<DB: DatabaseRef>(db: &DB, address: Address) -> Result<u128> {
        let cell = try_read_cell(&db, &address, &U256::from(4))?;
        let cell: u128 = cell.saturating_to();
        Ok(cell)
    }

    pub fn ticks_liquidity_net<DB: DatabaseRef>(db: &DB, address: Address, tick: i32) -> Result<i128> {
        //i24
        let cell = try_read_hashmap_cell(&db, &address, &U256::from(5), &U256::from_be_bytes(I256::try_from(tick)?.to_be_bytes::<32>()))?;
        let unsigned_liqudity: Uint<128, 2> = cell.shr(U256::from(128)).to();
        let signed_liquidity: Signed<128, 2> = Signed::<128, 2>::from_raw(unsigned_liqudity);
        let lu128: u128 = unsigned_liqudity.to();
        let li128: i128 = lu128 as i128;
        trace!("ticks_liquidity_net {address} {tick} {cell} -> {signed_liquidity}");

        Ok(li128)
    }
    pub fn tick_bitmap<DB: DatabaseRef>(db: &DB, address: Address, tick: i16) -> Result<U256> {
        //i16
        let cell = try_read_hashmap_cell(&db, &address, &U256::from(6), &U256::from_be_bytes(I256::try_from(tick)?.to_be_bytes::<32>()))?;
        trace!("tickBitmap {address} {tick} {cell}");
        Ok(cell)
    }

    pub fn position_info<DB: DatabaseRef>(db: &DB, address: Address, position: B256) -> Result<U256> {
        //i16
        let position: U256 = position.into();
        let cell = try_read_hashmap_cell(db, &address, &U256::from(7), &position)?;
        Ok(cell)
    }

    pub fn observations<DB: DatabaseRef>(db: &DB, address: Address, idx: u32) -> Result<U256> {
        //i16
        let cell = try_read_hashmap_cell(&db, &address, &U256::from(7), &U256::from(idx))?;
        Ok(cell)
    }

    pub fn slot0<DB: DatabaseRef>(db: &DB, address: Address) -> Result<slot0Return> {
        let cell = try_read_cell(&db, &address, &U256::from(0))?;
        let tick: Uint<24, 1> = ((Shr::<U256>::shr(cell, U256::from(160))) & *BITS24MASK).to();
        let tick: Signed<24, 1> = Signed::<24, 1>::from_raw(tick);
        let tick: i32 = tick.as_i32();

        let sqrt_price_x96: U160 = cell.bitand(*BITS160MASK).to();

        Ok(slot0Return {
            sqrtPriceX96: sqrt_price_x96,
            tick: tick.try_into()?,
            observationIndex: ((Shr::<U256>::shr(cell, U256::from(160 + 24))) & *BITS16MASK).to(),
            observationCardinality: ((Shr::<U256>::shr(cell, U256::from(160 + 24 + 16))) & *BITS16MASK).to(),
            observationCardinalityNext: ((Shr::<U256>::shr(cell, U256::from(160 + 24 + 16 + 16))) & *BITS16MASK).to(),
            feeProtocol: ((Shr::<U256>::shr(cell, U256::from(160 + 24 + 16 + 16 + 16))) & *BITS8MASK).to(),
            unlocked: ((Shr::<U256>::shr(cell, U256::from(160 + 24 + 16 + 16 + 16 + 8))) & *BITS1MASK).to(),
        })
    }
}

#[cfg(test)]
mod test {
    use alloy_primitives::Address;
    use eyre::Result;
    use revm::primitives::Env;
    use std::env;
    use tracing::debug;

    use loom_defi_address_book::UniswapV3PoolAddress;
    use loom_evm_db::LoomDBType;
    use loom_node_debug_provider::AnvilDebugProviderFactory;
    use loom_types_entities::required_state::RequiredStateReader;
    use loom_types_entities::{MarketState, Pool};

    use crate::db_reader::UniswapV3DBReader;
    use crate::state_readers::UniswapV3StateReader;
    use crate::UniswapV3Pool;

    #[tokio::test]
    async fn test_reader() -> Result<()> {
        let _ = env_logger::try_init_from_env(env_logger::Env::default().default_filter_or(
            "info,loom_types_entities::required_state=off,loom_types_blockchain::state_update=off,alloy_rpc_client::call=off,tungstenite=off",
        ));

        let node_url = env::var("MAINNET_WS")?;

        let client = AnvilDebugProviderFactory::from_node_on_block(node_url, 20038285).await?;

        let mut market_state = MarketState::new(LoomDBType::default());

        let pool_address: Address = UniswapV3PoolAddress::USDC_WETH_500;

        let pool = UniswapV3Pool::fetch_pool_data(client.clone(), pool_address).await?;

        let state_required = pool.get_state_required()?;

        let state_required = RequiredStateReader::fetch_calls_and_slots(client.clone(), state_required, None).await?;

        market_state.state_db.apply_geth_update(state_required);

        let evm_env = Env::default();

        let factory_evm = UniswapV3StateReader::factory(&market_state.state_db, evm_env.clone(), pool_address)?;
        let token0_evm = UniswapV3StateReader::token0(&market_state.state_db, evm_env.clone(), pool_address)?;
        let token1_evm = UniswapV3StateReader::token1(&market_state.state_db, evm_env.clone(), pool_address)?;

        debug!("{factory_evm:?} {token0_evm:?} {token1_evm:?}");

        let slot0_evm = UniswapV3StateReader::slot0(&market_state.state_db, evm_env.clone(), pool_address)?;

        let slot0_db = UniswapV3DBReader::slot0(&market_state.state_db, pool_address)?;

        debug!("evm : {slot0_evm:?}");
        debug!("db  : {slot0_db:?}");

        assert_eq!(slot0_evm, slot0_db);

        Ok(())
    }
}