Skip to content

Commit

Permalink
Merge pull request #3 from shuhuiluo/ephemeral
Browse files Browse the repository at this point in the history
Add `EphemeralTickDataProvider` for single `eth_call` tick fetch
  • Loading branch information
shuhuiluo authored Jan 3, 2024
2 parents 845775e + a7ddab3 commit 493ae36
Show file tree
Hide file tree
Showing 10 changed files with 971 additions and 106 deletions.
874 changes: 820 additions & 54 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "uniswap-v3-sdk-rs"
version = "0.5.0"
version = "0.6.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand All @@ -11,17 +11,19 @@ alloy-json-abi = "0.5.4"
alloy-primitives = "0.5.4"
alloy-sol-types = "0.5.4"
anyhow = "1.0"
aperture-lens = { git = "https://github.com/Aperture-Finance/Aperture-Lens", branch = "main" }
ethers = { version = "2.0", features = ["default"] }
num-bigint = "0.4.4"
num-traits = "0.2.17"
once_cell = "1.19.0"
ruint = "1.11.1"
thiserror = "1.0.53"
uniswap-sdk-core-rust = { git = "https://github.com/malik672/uniswap-sdk-core-rust", branch = "master" }
uniswap-sdk-core = { git = "https://github.com/malik672/uniswap-sdk-core-rust", branch = "master" }
uniswap_v3_math = "0.4.1"

[dev-dependencies]
criterion = "0.5.1"
ethers-core = "2.0.11"
tokio = { version = "1.35", features = ["full"] }

[[bench]]
name = "bit_math"
Expand Down
3 changes: 1 addition & 2 deletions benches/swap_math.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use alloy_primitives::{keccak256, I256, U256};
use alloy_sol_types::SolValue;
use criterion::{criterion_group, criterion_main, Criterion};
use ethers_core;
use uniswap_v3_math::{swap_math, utils::ruint_to_u256};
use uniswap_v3_sdk_rs::utils::compute_swap_step;

Expand Down Expand Up @@ -53,7 +52,7 @@ fn compute_swap_step_benchmark(c: &mut Criterion) {
}

fn compute_swap_step_benchmark_ref(c: &mut Criterion) {
use ethers_core::types::{I256, U256};
use ethers::types::{I256, U256};

let inputs: Vec<(U256, U256, u128, I256, u32)> = generate_inputs()
.into_iter()
Expand Down
2 changes: 1 addition & 1 deletion src/entities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ mod tick_list_data_provider;

pub use pool::Pool;
pub use tick::{Tick, TickTrait};
pub use tick_data_provider::{NoTickDataError, NoTickDataProvider, TickDataProvider};
pub use tick_data_provider::*;
pub use tick_list_data_provider::TickListDataProvider;
31 changes: 10 additions & 21 deletions src/entities/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ use crate::{
use alloy_primitives::{Address, B256, U256};
use num_bigint::BigUint;
use once_cell::sync::Lazy;
use uniswap_sdk_core_rust::{
entities::fractions::price::Price,
prelude::{BaseCurrency, CurrencyAmount, CurrencyTrait, Token},
};
use uniswap_sdk_core::prelude::*;

static _Q192: Lazy<BigUint> = Lazy::new(|| u256_to_big_uint(Q192));

Expand Down Expand Up @@ -172,34 +169,26 @@ impl Pool {
#[cfg(test)]
mod tests {
use super::*;
use uniswap_sdk_core_rust::prelude::WETH9;
use uniswap_sdk_core::token;

const ONE_ETHER: U256 = U256::from_limbs([10u64.pow(18), 0, 0, 0]);

static USDC: Lazy<Token> = Lazy::new(|| {
Token::new(
token!(
1,
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
.parse()
.unwrap(),
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
6,
Some("USDC".to_owned()),
Some("USD Coin".to_owned()),
None,
None,
"USDC",
"USD Coin"
)
});
static _DAI: Lazy<Token> = Lazy::new(|| {
Token::new(
token!(
1,
"0x6B175474E89094C44Da98b954EedeAC495271d0F"
.parse()
.unwrap(),
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
18,
Some("DAI".to_owned()),
Some("DAI Stablecoin".to_owned()),
None,
None,
"DAI",
"DAI Stablecoin"
)
});

Expand Down
115 changes: 115 additions & 0 deletions src/extensions/ephemeral_tick_data_provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use crate::{
entities::{Tick, TickDataProvider},
utils::{TickList, MAX_TICK, MIN_TICK},
};
use alloy_primitives::Address;
use anyhow::Result;
use aperture_lens::prelude::get_populated_ticks_in_range;
use ethers::prelude::{BlockId, ContractError, Middleware};
use std::sync::Arc;

/// A data provider for ticks that fetches ticks using an ephemeral contract in a single `eth_call`.
#[derive(Clone)]
pub struct EphemeralTickDataProvider<M: Middleware> {
pub pool: Address,
client: Arc<M>,
pub tick_lower: i32,
pub tick_upper: i32,
pub block_id: Option<BlockId>,
pub ticks: Vec<Tick>,
}

impl<M: Middleware> EphemeralTickDataProvider<M> {
pub fn new(
pool: Address,
client: Arc<M>,
tick_lower: Option<i32>,
tick_upper: Option<i32>,
block_id: Option<BlockId>,
) -> Self {
Self {
pool,
tick_lower: tick_lower.unwrap_or(MIN_TICK),
tick_upper: tick_upper.unwrap_or(MAX_TICK),
client,
block_id,
ticks: Vec::new(),
}
}

pub async fn fetch(&mut self) -> Result<(), ContractError<M>> {
let ticks = get_populated_ticks_in_range(
self.pool.into_array().into(),
self.tick_lower,
self.tick_upper,
self.client.clone(),
self.block_id,
)
.await?;
self.ticks = ticks
.into_iter()
.map(|tick| Tick::new(tick.tick, tick.liquidity_gross, tick.liquidity_net))
.collect();
Ok(())
}
}

impl<M: Middleware> TickDataProvider<Tick> for EphemeralTickDataProvider<M> {
fn get_tick(&self, tick: i32) -> Result<&Tick> {
Ok(self.ticks.get_tick(tick))
}

fn next_initialized_tick_within_one_word(
&self,
tick: i32,
lte: bool,
tick_spacing: i32,
) -> Result<(i32, bool)> {
Ok(self
.ticks
.next_initialized_tick_within_one_word(tick, lte, tick_spacing))
}
}

#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::address;
use ethers::prelude::{Http, Provider, MAINNET};
use once_cell::sync::Lazy;

static PROVIDER: Lazy<EphemeralTickDataProvider<Provider<Http>>> = Lazy::new(|| {
let provider = Arc::new(MAINNET.provider());
EphemeralTickDataProvider::new(
address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
provider,
None,
None,
Some(BlockId::from(17000000)),
)
});
const TICK_SPACING: i32 = 10;

#[tokio::test]
async fn test_ephemeral_tick_data_provider() -> Result<()> {
let mut provider = PROVIDER.clone();
provider.fetch().await?;
assert!(!provider.ticks.is_empty());
provider.ticks.validate_list(TICK_SPACING);
let tick = provider.get_tick(-92110)?;
assert_eq!(tick.liquidity_gross, 398290794261);
assert_eq!(tick.liquidity_net, 398290794261);
let (tick, success) = provider.next_initialized_tick_within_one_word(
MIN_TICK + TICK_SPACING,
true,
TICK_SPACING,
)?;
assert!(success);
assert_eq!(tick, -887270);
let (tick, success) =
provider.next_initialized_tick_within_one_word(0, false, TICK_SPACING)?;
assert!(success);
assert_eq!(tick, 100);
Ok(())
}
}
3 changes: 3 additions & 0 deletions src/extensions/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod ephemeral_tick_data_provider;

pub use ephemeral_tick_data_provider::EphemeralTickDataProvider;
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
pub mod constants;
pub mod entities;
pub mod extensions;
pub mod utils;
2 changes: 1 addition & 1 deletion src/utils/encode_sqrt_ratio_x96.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use num_bigint::BigInt;
use uniswap_sdk_core_rust::utils::sqrt::sqrt;
use uniswap_sdk_core::utils::sqrt::sqrt;

/// Returns the sqrt ratio as a Q64.96 corresponding to a given ratio of amount1 and amount0
///
Expand Down
38 changes: 14 additions & 24 deletions src/utils/price_tick_conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ use super::{
use alloy_primitives::U256;
use anyhow::Result;
use num_traits::ToBytes;
use uniswap_sdk_core_rust::{
entities::fractions::price::Price,
prelude::{FractionTrait, Token},
};
use uniswap_sdk_core::prelude::*;

/// Returns a price object corresponding to the input tick and the base/quote token.
/// Inputs must be tokens because the address order is used to interpret the price represented by the tick.
Expand Down Expand Up @@ -73,41 +70,34 @@ pub fn price_to_closest_tick(price: Price<Token, Token>) -> Result<i32> {
#[cfg(test)]
mod tests {
use super::*;
use num_bigint::BigInt;
use once_cell::sync::Lazy;
use uniswap_sdk_core_rust::constants::Rounding;
use uniswap_sdk_core::token;

static TOKEN0: Lazy<Token> = Lazy::new(|| {
Token::new(
token!(
1,
"0x0000000000000000000000000000000000000000".to_string(),
"0x0000000000000000000000000000000000000000",
18,
Some("T0".to_string()),
Some("token0".to_string()),
None,
None,
"T0",
"token0"
)
});
static TOKEN1: Lazy<Token> = Lazy::new(|| {
Token::new(
token!(
1,
"0x1111111111111111111111111111111111111111".to_string(),
"0x1111111111111111111111111111111111111111",
18,
Some("T1".to_string()),
Some("token1".to_string()),
None,
None,
"T1",
"token1"
)
});
static TOKEN2_6DECIMALS: Lazy<Token> = Lazy::new(|| {
Token::new(
token!(
1,
"0x2222222222222222222222222222222222222222".to_string(),
"0x2222222222222222222222222222222222222222",
6,
Some("T2".to_string()),
Some("token2".to_string()),
None,
None,
"T2",
"token2"
)
});

Expand Down

0 comments on commit 493ae36

Please sign in to comment.