Skip to content

Commit

Permalink
chore(refactor): Refactor and improve documentation (#40)
Browse files Browse the repository at this point in the history
* Update package versions and reorganize imports

The 'uniswap-v3-sdk' version has been updated to 0.25.0, along with other dependencies like 'base64' and 'ruint'. The code has also been revised to use 'alloy_primitives' for 'hex' and 'uint' imports, to bring some consistency to the modular imports. Changes have also been made on how 'pool' features are imported in 'entities/mod.rs'.

* Add benchmarks to README

A section for performance benchmarks has been added to the README file. It includes a detailed table showing the time taken and reference time for different functions, which can be expanded for viewing.

* Refactor pool, route and position entities code

Removed dependence on mutable self in methods for Pool, Route, and Position objects. Price-deriving methods in the Pool object were simplified to always calculate price instead of storing it. Also, the Debug and PartialEq trait implementations were relocated to follow Rust idioms.

* Add features section to documentation

A "Features" section has been added to the initial comments in the lib.rs file. This section outlines several important functionalities of the Rust Uniswap V3 SDK, such as its usage of alloy-rs types, the reimplementation of math libraries, the unit tests, benchmarks and extended capabilities for additional Uniswap V3 related functionalities.

* Add doctest for `compute_pool_address`

The function compute_pool_address now includes comprehensive usage examples directly in its documentation. This provides clear, practical guidance for developers on how to use the function. The related tests that were previously in a separate module have been removed as they are now redundant.
  • Loading branch information
shuhuiluo authored Mar 2, 2024
1 parent a838941 commit 55a44fb
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 137 deletions.
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "uniswap-v3-sdk"
version = "0.24.2"
version = "0.25.0"
edition = "2021"
authors = ["Shuhui Luo <twitter.com/aureliano_law>"]
description = "Uniswap V3 SDK for Rust"
Expand All @@ -18,7 +18,7 @@ alloy-primitives = "0.6"
alloy-sol-types = "0.6"
anyhow = "1.0"
aperture-lens = { version = "0.4", optional = true }
base64 = { version = "0.21.7", optional = true }
base64 = { version = "0.22", optional = true }
bigdecimal = "0.4.2"
ethers = { version = "2.0", optional = true }
ethers-core = "2.0"
Expand All @@ -27,11 +27,11 @@ num-integer = "0.1.45"
num-traits = "0.2.17"
once_cell = "1.19"
regex = { version = "1.10", optional = true }
ruint = "1.11"
ruint = "1.12"
serde_json = { version = "1.0", optional = true }
thiserror = "1.0"
uniswap-sdk-core = "0.16.0"
uniswap_v3_math = "0.4.1"
uniswap-sdk-core = "0.16"
uniswap_v3_math = "0.4"

[features]
extensions = ["aperture-lens", "base64", "ethers", "regex", "serde_json"]
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,24 @@ expect. The error handling is still a work in progress.
an [ephemeral contract](https://github.com/Aperture-Finance/Aperture-Lens/blob/904101e4daed59e02fd4b758b98b0749e70b583b/contracts/EphemeralGetPopulatedTicksInRange.sol)
in a single `eth_call`

<details>
<summary>Expand to see the benchmarks</summary>

| Function | Time | Reference |
|------------------------|-----------|-----------|
| most_significant_bit | 8.3693 µs | 39.691 µs |
| least_significant_bit | 5.0592 µs | 16.619 µs |
| get_sqrt_ratio_at_tick | 5.2105 µs | 71.137 µs |
| get_tick_at_sqrt_ratio | 34.331 µs | 191.08 µs |

</details>

## Getting started

Add the following to your `Cargo.toml` file:

```toml
uniswap-v3-sdk = { version = "0.23.0", features = ["extensions"] }
uniswap-v3-sdk = { version = "0.25.0", features = ["extensions"] }
```

### Usage
Expand Down
2 changes: 1 addition & 1 deletion src/entities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mod tick_data_provider;
mod tick_list_data_provider;
mod trade;

pub use pool::Pool;
pub use pool::{get_address, Pool};
pub use position::{MintAmounts, Position};
pub use route::Route;
pub use tick::{Tick, TickTrait};
Expand Down
106 changes: 47 additions & 59 deletions src/entities/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,30 @@ pub struct Pool<P> {
pub liquidity: u128,
pub tick_current: i32,
pub tick_data_provider: P,
_token0_price: Option<Price<Token, Token>>,
_token1_price: Option<Price<Token, Token>>,
}

impl<P> fmt::Debug for Pool<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Pool")
.field("token0", &self.token0)
.field("token1", &self.token1)
.field("fee", &self.fee)
.field("sqrt_ratio_x96", &self.sqrt_ratio_x96)
.field("liquidity", &self.liquidity)
.field("tick_current", &self.tick_current)
.finish()
}
}

impl<P> PartialEq for Pool<P> {
fn eq(&self, other: &Self) -> bool {
self.token0 == other.token0
&& self.token1 == other.token1
&& self.fee == other.fee
&& self.sqrt_ratio_x96 == other.sqrt_ratio_x96
&& self.liquidity == other.liquidity
&& self.tick_current == other.tick_current
}
}

struct SwapState {
Expand Down Expand Up @@ -121,33 +143,25 @@ impl<P> Pool<P> {
}

/// Returns the current mid price of the pool in terms of token0, i.e. the ratio of token1 over token0
pub fn token0_price(&mut self) -> Price<Token, Token> {
self._token0_price.clone().unwrap_or_else(|| {
let sqrt_ratio_x96: BigUint = u256_to_big_uint(self.sqrt_ratio_x96);
let price = Price::new(
self.token0.clone(),
self.token1.clone(),
_Q192.clone(),
&sqrt_ratio_x96 * &sqrt_ratio_x96,
);
self._token0_price = Some(price.clone());
price
})
pub fn token0_price(&self) -> Price<Token, Token> {
let sqrt_ratio_x96: BigUint = u256_to_big_uint(self.sqrt_ratio_x96);
Price::new(
self.token0.clone(),
self.token1.clone(),
_Q192.clone(),
&sqrt_ratio_x96 * &sqrt_ratio_x96,
)
}

/// Returns the current mid price of the pool in terms of token1, i.e. the ratio of token0 over token1
pub fn token1_price(&mut self) -> Price<Token, Token> {
self._token1_price.clone().unwrap_or_else(|| {
let sqrt_ratio_x96: BigUint = u256_to_big_uint(self.sqrt_ratio_x96);
let price = Price::new(
self.token1.clone(),
self.token0.clone(),
&sqrt_ratio_x96 * &sqrt_ratio_x96,
_Q192.clone(),
);
self._token1_price = Some(price.clone());
price
})
pub fn token1_price(&self) -> Price<Token, Token> {
let sqrt_ratio_x96: BigUint = u256_to_big_uint(self.sqrt_ratio_x96);
Price::new(
self.token1.clone(),
self.token0.clone(),
&sqrt_ratio_x96 * &sqrt_ratio_x96,
_Q192.clone(),
)
}

/// Return the price of the given token in terms of the other token in the pool.
Expand All @@ -157,7 +171,7 @@ impl<P> Pool<P> {
/// * `token`: The token to return price of
///
/// returns: Price<Token, Token>
pub fn price_of(&mut self, token: &Token) -> Price<Token, Token> {
pub fn price_of(&self, token: &Token) -> Price<Token, Token> {
assert!(self.involves_token(token), "TOKEN");
if self.token0.equals(token) {
self.token0_price()
Expand Down Expand Up @@ -204,8 +218,6 @@ where
liquidity,
tick_current: get_tick_at_sqrt_ratio(sqrt_ratio_x96)?,
tick_data_provider,
_token0_price: None,
_token1_price: None,
})
}

Expand Down Expand Up @@ -416,30 +428,6 @@ where
}
}

impl<P> fmt::Debug for Pool<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Pool")
.field("token0", &self.token0)
.field("token1", &self.token1)
.field("fee", &self.fee)
.field("sqrt_ratio_x96", &self.sqrt_ratio_x96)
.field("liquidity", &self.liquidity)
.field("tick_current", &self.tick_current)
.finish()
}
}

impl<P> PartialEq for Pool<P> {
fn eq(&self, other: &Self) -> bool {
self.token0 == other.token0
&& self.token1 == other.token1
&& self.fee == other.fee
&& self.sqrt_ratio_x96 == other.sqrt_ratio_x96
&& self.liquidity == other.liquidity
&& self.tick_current == other.tick_current
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -542,7 +530,7 @@ mod tests {

#[test]
fn token0_price_returns_price_of_token0_in_terms_of_token1() -> Result<()> {
let mut pool = Pool::new(
let pool = Pool::new(
USDC.clone(),
DAI.clone(),
FeeAmount::LOW,
Expand All @@ -554,7 +542,7 @@ mod tests {
.to_significant(5, Rounding::RoundHalfUp)?,
"1.01"
);
let mut pool = Pool::new(
let pool = Pool::new(
DAI.clone(),
USDC.clone(),
FeeAmount::LOW,
Expand All @@ -571,7 +559,7 @@ mod tests {

#[test]
fn token1_price_returns_price_of_token1_in_terms_of_token0() -> Result<()> {
let mut pool = Pool::new(
let pool = Pool::new(
USDC.clone(),
DAI.clone(),
FeeAmount::LOW,
Expand All @@ -583,7 +571,7 @@ mod tests {
.to_significant(5, Rounding::RoundHalfUp)?,
"0.9901"
);
let mut pool = Pool::new(
let pool = Pool::new(
DAI.clone(),
USDC.clone(),
FeeAmount::LOW,
Expand All @@ -600,7 +588,7 @@ mod tests {

#[test]
fn price_of_returns_price_of_token_in_terms_of_other_token() {
let mut pool = Pool::new(
let pool = Pool::new(
USDC.clone(),
DAI.clone(),
FeeAmount::LOW,
Expand All @@ -615,7 +603,7 @@ mod tests {
#[test]
#[should_panic(expected = "TOKEN")]
fn price_of_throws_if_invalid_token() {
let mut pool = Pool::new(
let pool = Pool::new(
USDC.clone(),
DAI.clone(),
FeeAmount::LOW,
Expand Down
63 changes: 30 additions & 33 deletions src/entities/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ pub struct Position<P> {
_mint_amounts: Option<MintAmounts>,
}

impl<P> fmt::Debug for Position<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Position")
.field("pool", &self.pool)
.field("tick_lower", &self.tick_lower)
.field("tick_upper", &self.tick_upper)
.field("liquidity", &self.liquidity)
.finish()
}
}

impl<P> PartialEq for Position<P> {
fn eq(&self, other: &Self) -> bool {
self.pool == other.pool
&& self.tick_lower == other.tick_lower
&& self.tick_upper == other.tick_upper
&& self.liquidity == other.liquidity
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MintAmounts {
pub amount0: U256,
Expand Down Expand Up @@ -145,7 +165,7 @@ impl<P> Position<P> {
/// ## Returns
///
/// (sqrt_ratio_x96_lower, sqrt_ratio_x96_upper)
fn ratios_after_slippage(&mut self, slippage_tolerance: &Percent) -> (U256, U256) {
fn ratios_after_slippage(&self, slippage_tolerance: &Percent) -> (U256, U256) {
let one = Percent::new(1, 1);
let price_lower = self.pool.token0_price().as_fraction()
* ((one.clone() - slippage_tolerance.clone()).as_fraction());
Expand Down Expand Up @@ -250,10 +270,7 @@ impl<P> Position<P> {
/// ## Returns
///
/// The amounts, with slippage
pub fn burn_amounts_with_slippage(
&mut self,
slippage_tolerance: &Percent,
) -> Result<(U256, U256)> {
pub fn burn_amounts_with_slippage(&self, slippage_tolerance: &Percent) -> Result<(U256, U256)> {
// get lower/upper prices
let (sqrt_ratio_x96_lower, sqrt_ratio_x96_upper) =
self.ratios_after_slippage(slippage_tolerance);
Expand Down Expand Up @@ -421,26 +438,6 @@ impl<P> Position<P> {
}
}

impl<P> fmt::Debug for Position<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Position")
.field("pool", &self.pool)
.field("tick_lower", &self.tick_lower)
.field("tick_upper", &self.tick_upper)
.field("liquidity", &self.liquidity)
.finish()
}
}

impl<P> PartialEq for Position<P> {
fn eq(&self, other: &Self) -> bool {
self.pool == other.pool
&& self.tick_lower == other.tick_lower
&& self.tick_upper == other.tick_upper
&& self.liquidity == other.liquidity
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -704,7 +701,7 @@ mod tests {

#[test]
fn burn_amounts_with_slippage_is_correct_for_pool_at_min_price() {
let mut position = Position::new(
let position = Position::new(
Pool::new(DAI.clone(), USDC.clone(), FeeAmount::LOW, MIN_SQRT_RATIO, 0).unwrap(),
100e18 as u128,
nearest_usable_tick(*POOL_TICK_CURRENT, TICK_SPACING) + TICK_SPACING,
Expand All @@ -720,7 +717,7 @@ mod tests {

#[test]
fn burn_amounts_with_slippage_is_correct_for_pool_at_max_price() {
let mut position = Position::new(
let position = Position::new(
Pool::new(
DAI.clone(),
USDC.clone(),
Expand All @@ -743,7 +740,7 @@ mod tests {

#[test]
fn burn_amounts_with_slippage_is_correct_for_positions_below() {
let mut position = Position::new(
let position = Position::new(
DAI_USDC_POOL.clone(),
100e18 as u128,
nearest_usable_tick(*POOL_TICK_CURRENT, TICK_SPACING) + TICK_SPACING,
Expand All @@ -759,7 +756,7 @@ mod tests {

#[test]
fn burn_amounts_with_slippage_is_correct_for_positions_above() {
let mut position = Position::new(
let position = Position::new(
DAI_USDC_POOL.clone(),
100e18 as u128,
nearest_usable_tick(*POOL_TICK_CURRENT, TICK_SPACING) - TICK_SPACING * 2,
Expand All @@ -775,7 +772,7 @@ mod tests {

#[test]
fn burn_amounts_with_slippage_is_correct_for_positions_within() {
let mut position = Position::new(
let position = Position::new(
DAI_USDC_POOL.clone(),
100e18 as u128,
nearest_usable_tick(*POOL_TICK_CURRENT, TICK_SPACING) - TICK_SPACING * 2,
Expand All @@ -791,7 +788,7 @@ mod tests {

#[test]
fn burn_amounts_with_slippage_is_correct_for_positions_below_05_percent_slippage() {
let mut position = Position::new(
let position = Position::new(
DAI_USDC_POOL.clone(),
100e18 as u128,
nearest_usable_tick(*POOL_TICK_CURRENT, TICK_SPACING) + TICK_SPACING,
Expand All @@ -807,7 +804,7 @@ mod tests {

#[test]
fn burn_amounts_with_slippage_is_correct_for_positions_above_05_percent_slippage() {
let mut position = Position::new(
let position = Position::new(
DAI_USDC_POOL.clone(),
100e18 as u128,
nearest_usable_tick(*POOL_TICK_CURRENT, TICK_SPACING) - TICK_SPACING * 2,
Expand All @@ -823,7 +820,7 @@ mod tests {

#[test]
fn burn_amounts_with_slippage_is_correct_for_positions_within_05_percent_slippage() {
let mut position = Position::new(
let position = Position::new(
DAI_USDC_POOL.clone(),
100e18 as u128,
nearest_usable_tick(*POOL_TICK_CURRENT, TICK_SPACING) - TICK_SPACING * 2,
Expand Down
Loading

0 comments on commit 55a44fb

Please sign in to comment.