use crate::contracts_rpc::RpcAlgebraPoolFeeInState as AlgebraPoolFeeInState;
use crate::contracts_rpc::RpcAlgebraTwoSideFee as AlgebraTwoSideFee;
use crate::contracts_rpc::RpcAlgebraV3Pool as AlgebraV3Pool;
use crate::contracts_rpc::RpcCLPPool as CLPPool;
use crate::contracts_rpc::RpcIQuoter as IQuoter;
use crate::contracts_rpc::RpcIUniswapV3Pool as IUniswapV3Pool;
use crate::v3::{
get_ramses_quoter, is_ramses_factory, Tick, UniswapV3Pool, V3PoolType, MAX_TICK_I32,
MIN_TICK_I32, RAMSES_FACTOR,
};
use crate::{PoolInterface, TokenInfo};
use alloy::primitives::{aliases::U24, Address, Signed, U160, U256};
use alloy::primitives::{Uint, U128};
use alloy::{
eips::BlockId,
providers::{MulticallBuilder, Provider},
};
use anyhow::Result;
use log::info;
use std::collections::BTreeMap;
use std::sync::Arc;
pub async fn fetch_v3_pool<P: Provider + Send + Sync, T: TokenInfo>(
provider: &Arc<P>,
pool_address: Address,
block_number: BlockId,
token_info: &T,
multicall_address: Address,
chain_id: u64,
) -> Result<UniswapV3Pool> {
info!("[Chain {}] Fetching V3 pool: {}", chain_id, pool_address);
let mut v3_pool_type = V3PoolType::UniswapV3;
let uniswapv3_pool_instance = IUniswapV3Pool::new(pool_address, &provider);
let clp_pool_instance = CLPPool::new(pool_address, &provider);
let algebra_v3_pool_instance = AlgebraV3Pool::new(pool_address, &provider);
let algebra_two_side_fee_pool_instance = AlgebraTwoSideFee::new(pool_address, &provider);
let algebra_pool_fee_in_state_instance = AlgebraPoolFeeInState::new(pool_address, &provider);
let multicall_result = provider
.multicall()
.address(multicall_address)
.add(uniswapv3_pool_instance.token0()) .add(uniswapv3_pool_instance.token1()) .add(uniswapv3_pool_instance.fee()) .add(uniswapv3_pool_instance.tickSpacing()) .add(uniswapv3_pool_instance.slot0()) .add(uniswapv3_pool_instance.liquidity()) .add(uniswapv3_pool_instance.factory()) .add(clp_pool_instance.slot0()) .add(algebra_v3_pool_instance.fee()) .add(algebra_v3_pool_instance.globalState()) .add(algebra_two_side_fee_pool_instance.globalState()) .add(algebra_two_side_fee_pool_instance.activeIncentive()) .add(algebra_pool_fee_in_state_instance.globalState()) .block(block_number)
.try_aggregate(false)
.await?;
let (token0, token1, fee, tick_spacing, sqrt_price_x96, tick, liquidity, factory) =
if let Ok(slot0_result) = multicall_result.7 {
(
multicall_result
.0
.map_err(|e| anyhow::anyhow!("token0() failed for {}: {}", pool_address, e))?,
multicall_result
.1
.map_err(|e| anyhow::anyhow!("token1() failed for {}: {}", pool_address, e))?,
multicall_result
.2
.map_err(|e| anyhow::anyhow!("fee() failed for {}: {}", pool_address, e))?,
multicall_result.3.map_err(|e| {
anyhow::anyhow!("tickSpacing() failed for {}: {}", pool_address, e)
})?,
slot0_result.sqrtPriceX96,
slot0_result.tick,
multicall_result.5.map_err(|e| {
anyhow::anyhow!("liquidity() failed for {}: {}", pool_address, e)
})?,
multicall_result
.6
.map_err(|e| anyhow::anyhow!("factory() failed for {}: {}", pool_address, e))?,
)
} else if let Ok(slot0_result) = multicall_result.12 {
let fee: U24 = U24::from(slot0_result.fee);
v3_pool_type = V3PoolType::AlgebraPoolFeeInState;
(
multicall_result
.0
.map_err(|e| anyhow::anyhow!("token0() failed for {}: {}", pool_address, e))?,
multicall_result
.1
.map_err(|e| anyhow::anyhow!("token1() failed for {}: {}", pool_address, e))?,
fee,
multicall_result.3.map_err(|e| {
anyhow::anyhow!("tickSpacing() failed for {}: {}", pool_address, e)
})?,
slot0_result.price,
slot0_result.tick,
multicall_result.5.map_err(|e| {
anyhow::anyhow!("liquidity() failed for {}: {}", pool_address, e)
})?,
multicall_result
.6
.map_err(|e| anyhow::anyhow!("factory() failed for {}: {}", pool_address, e))?,
)
} else if let Ok(_) = multicall_result.11 {
let slot0_result = multicall_result.10.map_err(|e| {
anyhow::anyhow!(
"globalState() [AlgebraTwoSideFee] failed for {}: {}",
pool_address,
e
)
})?;
let fee: U24 = if slot0_result.feeZto > slot0_result.feeOtz {
U24::from(slot0_result.feeZto)
} else {
U24::from(slot0_result.feeOtz)
};
v3_pool_type = V3PoolType::AlgebraTwoSideFee;
(
multicall_result
.0
.map_err(|e| anyhow::anyhow!("token0() failed for {}: {}", pool_address, e))?,
multicall_result
.1
.map_err(|e| anyhow::anyhow!("token1() failed for {}: {}", pool_address, e))?,
fee,
multicall_result.3.map_err(|e| {
anyhow::anyhow!("tickSpacing() failed for {}: {}", pool_address, e)
})?,
slot0_result.price,
slot0_result.tick,
multicall_result.5.map_err(|e| {
anyhow::anyhow!("liquidity() failed for {}: {}", pool_address, e)
})?,
multicall_result
.6
.map_err(|e| anyhow::anyhow!("factory() failed for {}: {}", pool_address, e))?,
)
} else if let Ok(slot0_result) = multicall_result.9 {
v3_pool_type = V3PoolType::AlgebraV3;
(
multicall_result
.0
.map_err(|e| anyhow::anyhow!("token0() failed for {}: {}", pool_address, e))?,
multicall_result
.1
.map_err(|e| anyhow::anyhow!("token1() failed for {}: {}", pool_address, e))?,
U24::from(multicall_result.8.map_err(|e| {
anyhow::anyhow!("fee() [AlgebraV3] failed for {}: {}", pool_address, e)
})?),
multicall_result.3.map_err(|e| {
anyhow::anyhow!("tickSpacing() failed for {}: {}", pool_address, e)
})?,
slot0_result.price,
slot0_result.tick,
multicall_result.5.map_err(|e| {
anyhow::anyhow!("liquidity() failed for {}: {}", pool_address, e)
})?,
multicall_result
.6
.map_err(|e| anyhow::anyhow!("factory() failed for {}: {}", pool_address, e))?,
)
} else {
let slot0_result = multicall_result
.4
.map_err(|e| anyhow::anyhow!("slot0() failed for {}: {}", pool_address, e))?;
let factory = multicall_result
.6
.map_err(|e| anyhow::anyhow!("factory() failed for {}: {}", pool_address, e))?;
if is_ramses_factory(factory) {
v3_pool_type = V3PoolType::RamsesV2;
}
(
multicall_result
.0
.map_err(|e| anyhow::anyhow!("token0() failed for {}: {}", pool_address, e))?,
multicall_result
.1
.map_err(|e| anyhow::anyhow!("token1() failed for {}: {}", pool_address, e))?,
multicall_result
.2
.map_err(|e| anyhow::anyhow!("fee() failed for {}: {}", pool_address, e))?,
multicall_result.3.map_err(|e| {
anyhow::anyhow!("tickSpacing() failed for {}: {}", pool_address, e)
})?,
slot0_result.sqrtPriceX96,
slot0_result.tick,
multicall_result.5.map_err(|e| {
anyhow::anyhow!("liquidity() failed for {}: {}", pool_address, e)
})?,
factory,
)
};
let (token0, _) = token_info
.get_or_fetch_token(provider, token0, multicall_address)
.await?;
let (token1, _) = token_info
.get_or_fetch_token(provider, token1, multicall_address)
.await?;
info!(
"[Chain {}] V3 Pool {:?}: Token0: {}, Token1: {}, Fee: {}, Factory: {}, Tick: {}, Liquidity: {}",
chain_id, v3_pool_type, token0, token1, fee, factory, tick, liquidity
);
let mut pool = UniswapV3Pool::new(
pool_address,
token0,
token1,
fee,
tick_spacing.as_i32(),
sqrt_price_x96.to::<U160>(),
tick.as_i32(),
liquidity,
factory,
v3_pool_type,
);
fetch_v3_ticks(provider, &mut pool, block_number, multicall_address).await?;
if pool.pool_type == V3PoolType::RamsesV2 {
let ratio_conversion_factor =
calculate_ratio_conversion_factor(&pool, provider, block_number, chain_id).await?;
info!(
"[Chain {}] Ratio conversion factor: {}",
chain_id,
ratio_conversion_factor.to::<U128>()
);
pool.update_ratio_conversion_factor(ratio_conversion_factor);
}
Ok(pool)
}
pub async fn fetch_v3_ticks<P: Provider + Send + Sync>(
provider: &Arc<P>,
pool: &mut UniswapV3Pool,
block_number: BlockId,
multicall_address: Address,
) -> Result<()> {
let mut tick_indices = Vec::new();
match pool.pool_type {
V3PoolType::UniswapV3 | V3PoolType::RamsesV2 | V3PoolType::PancakeV3 => {
let min_word = pool.tick_to_word(MIN_TICK_I32);
let max_word = pool.tick_to_word(MAX_TICK_I32);
let mut word_pos_indices: Vec<i32> = vec![];
let mut all_bitmaps = Vec::new();
let contract = IUniswapV3Pool::new(pool.address, provider);
for chunk in (min_word..=max_word).collect::<Vec<_>>().chunks(250) {
let mut multicall =
MulticallBuilder::new_dynamic(provider).address(multicall_address);
for &word_pos in chunk {
word_pos_indices.push(word_pos);
multicall = multicall.add_dynamic(contract.tickBitmap(word_pos as i16));
}
let results = multicall.block(block_number).aggregate().await?;
all_bitmaps.extend(results.into_iter().map(|tick_bitmap| tick_bitmap));
}
for (j, word_pos) in word_pos_indices.iter().enumerate() {
let bitmap = all_bitmaps[j];
if bitmap != U256::ZERO {
for i in 0..256 {
let bit = U256::from(1u64);
let initialized = (bitmap & (bit << i)) != U256::ZERO;
if initialized {
let tick_index = (word_pos * 256 + i as i32) * pool.tick_spacing;
tick_indices.push(tick_index);
}
}
}
}
}
V3PoolType::AlgebraV3 => {
let contract = AlgebraV3Pool::new(pool.address, provider);
let tick_tree_root: u32 = contract.tickTreeRoot().block(block_number).call().await?;
if tick_tree_root == 0 {
pool.ticks = BTreeMap::new();
return Ok(());
}
let mut second_layer_indices = Vec::new();
for root_bit in 0..32 {
if (tick_tree_root & (1 << root_bit)) != 0 {
second_layer_indices.push(root_bit as i16);
}
}
let mut second_layer_multicall =
MulticallBuilder::new_dynamic(provider).address(multicall_address);
for &second_layer_index in &second_layer_indices {
second_layer_multicall = second_layer_multicall
.add_dynamic(contract.tickTreeSecondLayer(second_layer_index));
}
let second_layer_results = second_layer_multicall
.block(block_number)
.aggregate()
.await?;
let mut tick_table_indices = Vec::new();
const SECOND_LAYER_OFFSET: i16 = 3466;
for (i, &second_layer_index) in second_layer_indices.iter().enumerate() {
let second_layer_bitmap: U256 = second_layer_results[i];
if second_layer_bitmap != U256::ZERO {
for second_bit in 0..256 {
if (second_layer_bitmap & (U256::from(1u64) << second_bit)) != U256::ZERO {
let leaf_index = second_layer_index as i32 * 256 + second_bit as i32;
let tick_table_index = leaf_index - SECOND_LAYER_OFFSET as i32;
tick_table_indices.push(tick_table_index as i16);
}
}
}
}
let mut tick_table_multicall =
MulticallBuilder::new_dynamic(provider).address(multicall_address);
for &tick_table_index in &tick_table_indices {
tick_table_multicall =
tick_table_multicall.add_dynamic(contract.tickTable(tick_table_index));
}
let tick_table_results = tick_table_multicall.block(block_number).aggregate().await?;
for (i, &tick_table_index) in tick_table_indices.iter().enumerate() {
let tick_table_bitmap: U256 = tick_table_results[i];
if tick_table_bitmap != U256::ZERO {
for tick_bit in 0..256 {
if (tick_table_bitmap & (U256::from(1u64) << tick_bit)) != U256::ZERO {
let tick_index = (tick_table_index as i32)
.wrapping_mul(256)
.wrapping_add(tick_bit as i32);
if tick_index >= MIN_TICK_I32 && tick_index <= MAX_TICK_I32 {
tick_indices.push(tick_index);
}
}
}
}
}
}
V3PoolType::AlgebraTwoSideFee | V3PoolType::AlgebraPoolFeeInState => {
let min_word = pool.tick_to_word(MIN_TICK_I32);
let max_word = pool.tick_to_word(MAX_TICK_I32);
let mut word_pos_indices: Vec<i32> = vec![];
let mut all_bitmaps = Vec::new();
let contract = AlgebraTwoSideFee::new(pool.address, provider);
for chunk in (min_word..=max_word).collect::<Vec<_>>().chunks(250) {
let mut multicall =
MulticallBuilder::new_dynamic(provider).address(multicall_address);
for &word_pos in chunk {
word_pos_indices.push(word_pos);
multicall = multicall.add_dynamic(contract.tickTable(word_pos as i16));
}
let results = multicall.block(block_number).aggregate().await?;
all_bitmaps.extend(results.into_iter().map(|tick_bitmap| tick_bitmap));
}
for (j, word_pos) in word_pos_indices.iter().enumerate() {
let bitmap = all_bitmaps[j];
if bitmap != U256::ZERO {
for i in 0..256 {
let bit = U256::from(1u64);
let initialized = (bitmap & (bit << i)) != U256::ZERO;
if initialized {
let tick_index = (word_pos * 256 + i as i32) * pool.tick_spacing;
tick_indices.push(tick_index);
}
}
}
}
}
}
let mut all_ticks: BTreeMap<i32, Tick> = BTreeMap::new();
match pool.pool_type {
V3PoolType::UniswapV3 | V3PoolType::RamsesV2 | V3PoolType::PancakeV3 => {
let contract = IUniswapV3Pool::new(pool.address, provider);
for chunk in tick_indices.chunks(250) {
let mut multicall =
MulticallBuilder::new_dynamic(provider).address(multicall_address);
for &tick_index in chunk {
multicall = multicall.add_dynamic(
contract.ticks(Signed::<24, 1>::try_from(tick_index).unwrap()),
);
}
let results = multicall.block(block_number).aggregate().await?;
for (i, tick_index) in chunk.iter().enumerate() {
let tick_response = &results[i];
let tick = Tick {
index: *tick_index,
liquidity_gross: tick_response.liquidityGross,
liquidity_net: tick_response.liquidityNet,
};
all_ticks.insert(*tick_index, tick);
}
}
}
V3PoolType::AlgebraV3
| V3PoolType::AlgebraTwoSideFee
| V3PoolType::AlgebraPoolFeeInState => {
let contract = AlgebraV3Pool::new(pool.address, provider);
for chunk in tick_indices.chunks(250) {
let mut multicall =
MulticallBuilder::new_dynamic(provider).address(multicall_address);
for &tick_index in chunk {
multicall = multicall.add_dynamic(
contract.ticks(Signed::<24, 1>::try_from(tick_index).unwrap()),
);
}
let results = multicall.block(block_number).aggregate().await?;
for (i, tick_index) in chunk.iter().enumerate() {
let tick_response = &results[i];
let tick = Tick {
index: *tick_index,
liquidity_gross: tick_response.liquidityTotal.to::<U128>().to::<u128>(),
liquidity_net: tick_response.liquidityDelta,
};
all_ticks.insert(*tick_index, tick);
}
}
}
}
pool.ticks = all_ticks;
Ok(())
}
pub async fn calculate_ratio_conversion_factor<P: Provider + Send + Sync>(
pool_v3: &UniswapV3Pool,
provider: &Arc<P>,
block_number: BlockId,
chain_id: u64,
) -> Result<U256> {
let quoter = get_ramses_quoter(pool_v3.factory);
if let Some(quoter) = quoter {
let quoter_instance = IQuoter::new(quoter, &provider);
let amount_in = U256::from(100000000000u64);
let ratio_conversion_factor_0 = match quoter_instance
.quoteExactInputSingle(
pool_v3.token0,
pool_v3.token1,
U24::from(pool_v3.fee),
amount_in,
Uint::from(0),
)
.call()
.block(block_number)
.await
{
Ok(amount_out_0) => {
let amount_out_estimate_0 = pool_v3
.calculate_output(&pool_v3.token0, amount_in)
.unwrap();
let ratio_conversion_factor_0 = if amount_out_estimate_0 == U256::ZERO {
U256::MAX
} else if amount_out_0 == amount_out_estimate_0 {
U256::from(RAMSES_FACTOR)
} else {
amount_out_0 * U256::from(RAMSES_FACTOR) / amount_out_estimate_0 - U256::ONE
};
info!(
"[Chain {}] Ratio conversion factor 0: {}",
chain_id, ratio_conversion_factor_0
);
ratio_conversion_factor_0
}
Err(_) => {
info!(
"[Chain {}] Failed to fetch ratio conversion factor 0",
chain_id
);
U256::from(RAMSES_FACTOR)
}
};
let ratio_conversion_factor_1 = match quoter_instance
.quoteExactInputSingle(
pool_v3.token1,
pool_v3.token0,
U24::from(pool_v3.fee),
amount_in,
Uint::from(0),
)
.call()
.block(block_number)
.await
{
Ok(amount_out_1) => {
let amount_out_estimate_1 = pool_v3
.calculate_output(&pool_v3.token1, amount_in)
.unwrap();
let ratio_conversion_factor_1 = if amount_out_estimate_1 == U256::ZERO {
U256::MAX
} else if amount_out_1 == amount_out_estimate_1 {
U256::from(RAMSES_FACTOR)
} else {
amount_out_1 * U256::from(RAMSES_FACTOR) / amount_out_estimate_1 - U256::ONE
};
info!(
"[Chain {}] Ratio conversion factor 1: {}",
chain_id, ratio_conversion_factor_1
);
ratio_conversion_factor_1
}
Err(_) => {
info!(
"[Chain {}] Failed to fetch ratio conversion factor 1",
chain_id
);
U256::from(RAMSES_FACTOR)
}
};
if ratio_conversion_factor_0 == U256::MAX && ratio_conversion_factor_1 == U256::MAX {
Ok(U256::from(RAMSES_FACTOR))
} else {
Ok(ratio_conversion_factor_0.min(ratio_conversion_factor_1))
}
} else {
Ok(U256::from(RAMSES_FACTOR))
}
}