From 52fed4e254b5eb302c2b23f16e14f047c25c42c2 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 31 Jan 2025 16:34:47 -0500 Subject: [PATCH] will code --- .../src/matching/uniswap/poolsnapshot.rs | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/crates/types/src/matching/uniswap/poolsnapshot.rs b/crates/types/src/matching/uniswap/poolsnapshot.rs index 229df965d..2c78b6a9a 100644 --- a/crates/types/src/matching/uniswap/poolsnapshot.rs +++ b/crates/types/src/matching/uniswap/poolsnapshot.rs @@ -167,17 +167,26 @@ impl PoolSnapshot { let liq_range = self.ranges_for_ticks(start_tick, end_tick).ok()?; let mut res = 0; + let iter = if is_bid { liq_range.into_iter().rev().collect() } else { liq_range }; // bid is zero for 1 - for tick in liq_range { + for tick in iter { // bid means that price moving down let is_start_swap = (is_bid && tick.upper_tick == start_tick) || (!is_bid && tick.lower_tick == start_tick); + let is_end_swap = + (is_bid && tick.lower_tick <= end_tick && tick.upper_tick >= end_tick) + || (!is_bid && tick.upper_tick >= end_tick && tick.lower_tick <= end_tick); + // grab ratio range let (start, target) = if is_start_swap && is_bid { (self.sqrt_price_x96, SqrtPriceX96::at_tick(tick.lower_tick).ok()?) } else if is_start_swap && !is_bid { (self.sqrt_price_x96, SqrtPriceX96::at_tick(tick.upper_tick).ok()?) + } else if is_end_swap && is_bid { + (SqrtPriceX96::at_tick(tick.upper_tick).ok()?, end_price) + } else if is_end_swap && !is_bid { + (SqrtPriceX96::at_tick(tick.lower_tick).ok()?, end_price) } else if is_bid { ( SqrtPriceX96::at_tick(tick.upper_tick).ok()?, @@ -210,3 +219,126 @@ impl PoolSnapshot { Some(res) } } + +#[cfg(test)] +mod tests { + use super::*; + + fn setup_basic_pool() -> PoolSnapshot { + // Create a simple pool with three tick ranges + let ranges = vec![ + LiqRange { lower_tick: 0, upper_tick: 100, liquidity: 1000 }, + LiqRange { lower_tick: 100, upper_tick: 200, liquidity: 2000 }, + LiqRange { lower_tick: 200, upper_tick: 300, liquidity: 1500 }, + ]; + + // Start price in the middle range (tick 150) + let sqrt_price_x96 = SqrtPriceX96::at_tick(150).unwrap(); + PoolSnapshot::new(ranges, sqrt_price_x96).unwrap() + } + + #[test] + fn test_get_deltas_bid_direction() { + let pool = setup_basic_pool(); + + // Test moving from tick 150 to tick 250 (upward/bid movement) + let start_price = SqrtPriceX96::at_tick(150).unwrap(); + let end_price = SqrtPriceX96::at_tick(250).unwrap(); + + let delta = pool.get_deltas(start_price, end_price).unwrap(); + + // We should get a non-zero amount + assert!(delta > 0, "Delta should be positive for bid direction"); + + // Test the reverse direction to ensure it's different + let reverse_delta = pool.get_deltas(end_price, start_price).unwrap(); + assert_ne!(delta, reverse_delta, "Bid and ask deltas should differ"); + } + + #[test] + fn test_get_deltas_within_same_range() { + let pool = setup_basic_pool(); + + // Test movement within the same tick range (150 to 180) + let start_price = SqrtPriceX96::at_tick(150).unwrap(); + let end_price = SqrtPriceX96::at_tick(180).unwrap(); + + let delta = pool.get_deltas(start_price, end_price).unwrap(); + assert!(delta > 0, "Delta should be positive within same range"); + + // Test a smaller price movement + let small_end_price = SqrtPriceX96::at_tick(160).unwrap(); + let small_delta = pool.get_deltas(start_price, small_end_price).unwrap(); + assert!(small_delta < delta, "Smaller price movement should result in smaller delta"); + } + + #[test] + fn test_get_deltas_cross_multiple_ranges() { + let pool = setup_basic_pool(); + + // Test movement across multiple ranges (50 to 250) + let start_price = SqrtPriceX96::at_tick(50).unwrap(); + let end_price = SqrtPriceX96::at_tick(250).unwrap(); + + let delta = pool.get_deltas(start_price, end_price).unwrap(); + + // Test movement within a single range + let single_range_start = SqrtPriceX96::at_tick(120).unwrap(); + let single_range_end = SqrtPriceX96::at_tick(180).unwrap(); + let single_range_delta = pool + .get_deltas(single_range_start, single_range_end) + .unwrap(); + + assert!( + delta > single_range_delta, + "Cross-range delta should be larger than single range delta" + ); + } + + #[test] + fn test_get_deltas_edge_cases() { + let pool = setup_basic_pool(); + + // Test movement at range boundaries + let start_price = SqrtPriceX96::at_tick(100).unwrap(); // Exactly at a range boundary + let end_price = SqrtPriceX96::at_tick(200).unwrap(); // Another range boundary + + let delta = pool.get_deltas(start_price, end_price); + assert!(delta.is_some(), "Should handle range boundary movements"); + + // Test minimal movement + let tiny_move_end = SqrtPriceX96::at_tick(151).unwrap(); + let tiny_delta = pool.get_deltas(start_price, tiny_move_end).unwrap(); + assert!(tiny_delta > 0, "Should handle minimal price movements"); + } + + #[test] + fn test_get_deltas_liquidity_impact() { + // Create two pools with different liquidity profiles + let high_liq_ranges = vec![ + LiqRange { + lower_tick: 0, + upper_tick: 100, + liquidity: 10000 // 10x more liquidity + }, + LiqRange { lower_tick: 100, upper_tick: 200, liquidity: 20000 }, + ]; + + let low_liq_ranges = vec![ + LiqRange { lower_tick: 0, upper_tick: 100, liquidity: 1000 }, + LiqRange { lower_tick: 100, upper_tick: 200, liquidity: 2000 }, + ]; + + let sqrt_price_x96 = SqrtPriceX96::at_tick(50).unwrap(); + let high_liq_pool = PoolSnapshot::new(high_liq_ranges, sqrt_price_x96).unwrap(); + let low_liq_pool = PoolSnapshot::new(low_liq_ranges, sqrt_price_x96).unwrap(); + + let start_price = SqrtPriceX96::at_tick(50).unwrap(); + let end_price = SqrtPriceX96::at_tick(150).unwrap(); + + let high_liq_delta = high_liq_pool.get_deltas(start_price, end_price).unwrap(); + let low_liq_delta = low_liq_pool.get_deltas(start_price, end_price).unwrap(); + + assert!(high_liq_delta > low_liq_delta, "Higher liquidity should result in larger delta"); + } +}