From 4ea8f57dbb3ae3aabb906647f8da8c94e2076a9b Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Thu, 29 Aug 2024 00:14:47 -0400 Subject: [PATCH 1/4] Add alias types and upgrade dependencies Replaced primitive integer types with alias types (U24 and I24) for better clarity and consistency. Enabled `clippy::redundant_clone` lint. Updated `alloy` dependency and incremented package version to 0.2.0. --- Cargo.toml | 6 +++--- src/lib.rs | 1 + src/pool_lens.rs | 10 +++++----- src/position_lens.rs | 6 +++--- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1043929..9e89332 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniswap-lens" -version = "0.1.0" +version = "0.2.0" edition = "2021" authors = ["Shuhui Luo "] description = "Contains ephemeral lens contracts that can be called without deployment and their Rust interfaces." @@ -11,11 +11,11 @@ keywords = ["alloy", "ethereum", "solidity", "rust", "uniswap"] include = ["src/**/*.rs"] [dependencies] -alloy = { version = "0.2.0", features = ["contract", "rpc-types", "transports"] } +alloy = { version = "0.3.0", features = ["contract", "rpc-types", "transports"] } anyhow = "1" [dev-dependencies] -alloy = { version = "0.2.0", features = ["transport-http"] } +alloy = { version = "0.3.0", features = ["transport-http"] } dotenv = "0.15.0" futures = "0.3" once_cell = "1.19" diff --git a/src/lib.rs b/src/lib.rs index 16e87a5..5b643c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ missing_debug_implementations, unreachable_pub, clippy::missing_const_for_fn, + clippy::redundant_clone, rustdoc::all )] #![cfg_attr(not(test), warn(unused_crate_dependencies))] diff --git a/src/pool_lens.rs b/src/pool_lens.rs index f4848e0..fbd4b2a 100644 --- a/src/pool_lens.rs +++ b/src/pool_lens.rs @@ -18,7 +18,7 @@ use crate::{ use alloy::{ contract::Error, eips::BlockId, - primitives::{Address, Bytes}, + primitives::{aliases::I24, Address, Bytes}, providers::Provider, sol_types::SolCall, transports::{Transport, TransportError}, @@ -27,8 +27,8 @@ use anyhow::Result; pub async fn get_populated_ticks_in_range( pool: Address, - tick_lower: i32, - tick_upper: i32, + tick_lower: I24, + tick_upper: I24, provider: P, block_id: Option, ) -> Result> @@ -72,8 +72,8 @@ where pub async fn get_ticks_slots( pool: Address, - tick_lower: i32, - tick_upper: i32, + tick_lower: I24, + tick_upper: I24, provider: P, block_id: Option, ) -> Result> diff --git a/src/position_lens.rs b/src/position_lens.rs index 51cc3fc..b748a4c 100644 --- a/src/position_lens.rs +++ b/src/position_lens.rs @@ -94,7 +94,7 @@ mod tests { tests::*, }; use alloy::{ - primitives::{address, b256, keccak256, uint, B256}, + primitives::{address, aliases::U24, b256, keccak256, uint, B256}, sol_types::SolValue, }; @@ -119,7 +119,7 @@ mod tests { factory: Address, token_a: Address, token_b: Address, - fee: u32, + fee: U24, init_code_hash: B256, ) -> Address { let (token_0, token_1) = if token_a < token_b { @@ -127,7 +127,7 @@ mod tests { } else { (token_b, token_a) }; - let pool_key = (token_0, token_1, fee as i32); + let pool_key = (token_0, token_1, fee); factory.create2(keccak256(pool_key.abi_encode()), init_code_hash) } From 7cc7cc725cd87a9d337f98594dc512724f3c89dc Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Sat, 31 Aug 2024 23:41:13 -0400 Subject: [PATCH 2/4] Add detailed documentation to lens modules Enhance the position, pool, and storage lens modules in the code with comprehensive documentation. This includes descriptions of functionalities and explanations of arguments and return values for functions. Additionally, update the README and Cargo.toml to reflect the library's purpose and features accurately. --- Cargo.toml | 4 +-- README.md | 8 ++++- package.json | 3 +- src/lib.rs | 2 ++ src/pool_lens.rs | 69 +++++++++++++++++++++++++++++++++++++++++++- src/position_lens.rs | 40 +++++++++++++++++++++++++ src/storage_lens.rs | 5 ++++ 7 files changed, 126 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e89332..ac813eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,11 @@ name = "uniswap-lens" version = "0.2.0" edition = "2021" authors = ["Shuhui Luo "] -description = "Contains ephemeral lens contracts that can be called without deployment and their Rust interfaces." +description = "A library for querying Uniswap V3 using ephemeral lens contracts." license = "Apache-2.0" readme = "README.md" repository = "https://github.com/shuhuiluo/uniswap-lens-rs" -keywords = ["alloy", "ethereum", "solidity", "rust", "uniswap"] +keywords = ["alloy", "ethereum", "solidity", "uniswap"] include = ["src/**/*.rs"] [dependencies] diff --git a/README.md b/README.md index 196c967..58f2adf 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,10 @@ [![npm version](https://img.shields.io/npm/v/aperture-lens/latest.svg)](https://www.npmjs.com/package/aperture-lens/v/latest) [![crates.io](https://img.shields.io/crates/v/uniswap-lens.svg)](https://crates.io/crates/uniswap-lens) -Contains ephemeral lens contracts that can be called without deployment and their interfaces in various Web3 libraries. +A library for querying Uniswap V3 using ephemeral lens contracts. + +## Features + +- Lens contracts in Solidity using the revert-in-constructor pattern +- Rust SDK for querying lens contracts using [alloy-rs](https://github.com/alloy-rs) +- TypeScript SDK for querying lens contracts using viem diff --git a/package.json b/package.json index 2e35bd3..53b31ca 100644 --- a/package.json +++ b/package.json @@ -80,5 +80,6 @@ ], "endOfLine": "lf", "printWidth": 120 - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/lib.rs b/src/lib.rs index 5b643c5..b6efbcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ //! # uniswap-lens +//! +//! A library for querying Uniswap V3 using ephemeral lens contracts. #![warn( missing_copy_implementations, missing_debug_implementations, diff --git a/src/pool_lens.rs b/src/pool_lens.rs index fbd4b2a..513ae49 100644 --- a/src/pool_lens.rs +++ b/src/pool_lens.rs @@ -1,3 +1,7 @@ +//! ## Pool Lens +//! +//! The pool lens module provides functions to fetch pool details using ephemeral contracts. + use crate::{ bindings::{ ephemeralgetpopulatedticksinrange::EphemeralGetPopulatedTicksInRange::{ @@ -25,6 +29,19 @@ use alloy::{ }; use anyhow::Result; +/// Get the populated ticks in a tick range. +/// +/// ## Arguments +/// +/// * `pool`: The address of a V3 pool +/// * `tick_lower`: The lower tick boundary +/// * `tick_upper`: The upper tick boundary +/// * `provider`: The alloy provider +/// * `block_id`: Optional block number to query +/// +/// ## Returns +/// +/// A vector of populated ticks within the range pub async fn get_populated_ticks_in_range( pool: Address, tick_lower: I24, @@ -40,7 +57,10 @@ where provider, pool, tick_lower, tick_upper, ); match call_ephemeral_contract!(deploy_builder, getPopulatedTicksInRangeCall, block_id) { - Ok(getPopulatedTicksInRangeReturn { populatedTicks }) => Ok(populatedTicks), + Ok(getPopulatedTicksInRangeReturn { populatedTicks }) => Ok(populatedTicks + .into_iter() + .filter(|PopulatedTick { tick, .. }| *tick >= tick_lower && *tick <= tick_upper) + .collect()), Err(err) => Err(err.into()), } } @@ -55,6 +75,17 @@ macro_rules! get_pool_storage { }; } +/// Get the static storage slots of a pool. +/// +/// ## Arguments +/// +/// * `pool`: The address of a V3 pool +/// * `provider`: The alloy provider +/// * `block_id`: Optional block number to query +/// +/// ## Returns +/// +/// A vector of slots containing the storage data pub async fn get_static_slots( pool: Address, provider: P, @@ -70,6 +101,19 @@ where ) } +/// Get the storage slots in the `ticks` mapping between `tick_lower` and `tick_upper`. +/// +/// ## Arguments +/// +/// * `pool`: The address of a V3 pool +/// * `tick_lower`: The lower tick boundary +/// * `tick_upper`: The upper tick boundary +/// * `provider`: The alloy provider +/// * `block_id`: Optional block number to query +/// +/// ## Returns +/// +/// A vector of slots containing the storage data pub async fn get_ticks_slots( pool: Address, tick_lower: I24, @@ -86,6 +130,17 @@ where block_id ) } +/// Get the storage slots in the `tickBitmap` mapping. +/// +/// ## Arguments +/// +/// * `pool`: The address of a V3 pool +/// * `provider`: The alloy provider +/// * `block_id`: Optional block number to query +/// +/// ## Returns +/// +/// A vector of slots containing the storage data pub async fn get_tick_bitmap_slots( pool: Address, provider: P, @@ -100,6 +155,18 @@ where block_id ) } +/// Get the storage slots in the `positions` mapping. +/// +/// ## Arguments +/// +/// * `pool`: The address of a V3 pool +/// * `positions`: A vector of position keys +/// * `provider`: The alloy provider +/// * `block_id`: Optional block number to query +/// +/// ## Returns +/// +/// A vector of slots containing the storage data pub async fn get_positions_slots( pool: Address, positions: Vec, diff --git a/src/position_lens.rs b/src/position_lens.rs index b748a4c..c5c610b 100644 --- a/src/position_lens.rs +++ b/src/position_lens.rs @@ -1,3 +1,7 @@ +//! ## Position Lens +//! +//! The position lens module provides functions to fetch position details using ephemeral contracts. + use crate::{ bindings::{ ephemeralallpositionsbyowner::{ @@ -31,6 +35,18 @@ use alloy::{ }; use anyhow::Result; +/// Get the details of a position given the token ID. +/// +/// ## Arguments +/// +/// * `npm`: The address of the non-fungible position manager +/// * `token_id`: The token ID of the position +/// * `provider`: The alloy provider +/// * `block_id`: Optional block number to query +/// +/// ## Returns +/// +/// The position details pub async fn get_position_details( npm: Address, token_id: U256, @@ -48,6 +64,18 @@ where } } +/// Get the details of multiple positions given the token IDs. +/// +/// ## Arguments +/// +/// * `npm`: The address of the non-fungible position manager +/// * `token_ids`: The token IDs of the positions +/// * `provider`: The alloy provider +/// * `block_id`: Optional block number to query +/// +/// ## Returns +/// +/// The array of position details pub async fn get_positions( npm: Address, token_ids: Vec, @@ -65,6 +93,18 @@ where } } +/// Get all positions owned by an address. +/// +/// ## Arguments +/// +/// * `npm`: The address of the non-fungible position manager +/// * `owner`: The address of the owner +/// * `provider`: The alloy provider +/// * `block_id`: Optional block number to query +/// +/// ## Returns +/// +/// The array of position details pub async fn get_all_positions_by_owner( npm: Address, owner: Address, diff --git a/src/storage_lens.rs b/src/storage_lens.rs index 3316167..69f08e9 100644 --- a/src/storage_lens.rs +++ b/src/storage_lens.rs @@ -1,3 +1,8 @@ +//! ## Storage Lens +//! +//! The storage lens module provides a function to batch `eth_getStorageAt` RPC calls in a single +//! `eth_call` by overriding the target contract's deployed bytecode with `EphemeralStorageLens`. + use crate::bindings::ephemeralstoragelens::{ EphemeralStorageLens, EphemeralStorageLens::EphemeralStorageLensInstance, }; From 1108fc486f33fdd144e26d240a4e3929d5a6510d Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Sat, 31 Aug 2024 23:59:45 -0400 Subject: [PATCH 3/4] Fix test --- src/pool_lens.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pool_lens.rs b/src/pool_lens.rs index 513ae49..021148d 100644 --- a/src/pool_lens.rs +++ b/src/pool_lens.rs @@ -200,10 +200,11 @@ mod tests { let provider = PROVIDER.clone(); let pool = IUniswapV3PoolInstance::new(POOL_ADDRESS, provider.clone()); let tick_current = pool.slot0().block(*BLOCK_NUMBER).call().await?.tick; + let tick_spacing = pool.tickSpacing().block(*BLOCK_NUMBER).call().await?._0; let ticks = get_populated_ticks_in_range( POOL_ADDRESS, tick_current, - tick_current, + tick_current + (tick_spacing << 8), provider, Some(*BLOCK_NUMBER), ) From 3b56229f3c925ca6e34e052b91332d1d1511eb7f Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Sun, 1 Sep 2024 00:25:43 -0400 Subject: [PATCH 4/4] Remove PCSV3-specific tests Deleted tests related to PancakeSwap V3 for BSC in both TypeScript and Solidity test files. This includes the removal of the PCSV3-specific PoolLens and StorageLens tests as well as updates to the gas-snapshot file to reflect these deletions. --- .gas-snapshot | 39 +++---- test/foundry/PoolLens.t.sol | 69 ------------ test/foundry/PositionLens.t.sol | 22 +--- test/foundry/StorageLens.t.sol | 9 -- test/foundry/TickLens.t.sol | 9 -- test/hardhat/pcsv3_test.ts | 188 -------------------------------- 6 files changed, 15 insertions(+), 321 deletions(-) delete mode 100644 test/hardhat/pcsv3_test.ts diff --git a/.gas-snapshot b/.gas-snapshot index f34e310..b07e9d3 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,26 +1,13 @@ -PCSV3PoolLensTest:testFuzz_GetPositions(int24,int24) (runs: 17, μ: 1392200, ~: 1437447) -PCSV3PoolLensTest:test_GetPopulatedTicksInRange() (gas: 524480) -PCSV3PoolLensTest:test_GetPositions() (gas: 1348279) -PCSV3PoolLensTest:test_GetSlots() (gas: 151189) -PCSV3PoolLensTest:test_GetTickBitmap() (gas: 3370484) -PCSV3PositionLensTest:testFuzz_GetPosition(uint256) (runs: 17, μ: 274996, ~: 275218) -PCSV3PositionLensTest:test_AllPositions() (gas: 19719373) -PCSV3PositionLensTest:test_GetFeesOwed() (gas: 2791103) -PCSV3PositionLensTest:test_GetPositions() (gas: 1176049) -PCSV3PositionLensTest:test_GetTotalAmounts() (gas: 2794460) -PCSV3StorageLensTest:testFuzz_extsload(bytes32) (runs: 257, μ: 33810, ~: 33944) -PCSV3StorageLensTest:test_extsload() (gas: 66675) -PCSV3TickLensTest:test_GetPopulatedTicksInRange() (gas: 793601) -PoolLensTest:testFuzz_GetPositions(int24,int24) (runs: 17, μ: 1389477, ~: 1419421) -PoolLensTest:test_GetPopulatedTicksInRange() (gas: 4524074) -PoolLensTest:test_GetPositions() (gas: 1132031) -PoolLensTest:test_GetSlots() (gas: 3695636) -PoolLensTest:test_GetTickBitmap() (gas: 3385051) -PositionLensTest:testFuzz_GetPosition(uint256) (runs: 17, μ: 271318, ~: 272509) -PositionLensTest:test_AllPositions() (gas: 1137940) -PositionLensTest:test_GetFeesOwed() (gas: 2444915) -PositionLensTest:test_GetPositions() (gas: 940131) -PositionLensTest:test_GetTotalAmounts() (gas: 2456667) -StorageLensTest:testFuzz_extsload(bytes32) (runs: 17, μ: 33820, ~: 33944) -StorageLensTest:test_extsload() (gas: 66675) -TickLensTest:test_GetPopulatedTicksInRange() (gas: 11472791) \ No newline at end of file +PoolLensTest:testFuzz_GetPositions(int24,int24) (runs: 22, μ: 1364083, ~: 1404255) +PoolLensTest:test_GetPopulatedTicksInRange() (gas: 4544800) +PoolLensTest:test_GetPositions() (gas: 1132120) +PoolLensTest:test_GetSlots() (gas: 3710586) +PoolLensTest:test_GetTickBitmap() (gas: 3399608) +PositionLensTest:testFuzz_GetPosition(uint256) (runs: 22, μ: 270296, ~: 272574) +PositionLensTest:test_AllPositions() (gas: 1140527) +PositionLensTest:test_GetFeesOwed() (gas: 2444865) +PositionLensTest:test_GetPositions() (gas: 940419) +PositionLensTest:test_GetTotalAmounts() (gas: 2456617) +StorageLensTest:testFuzz_extsload(bytes32) (runs: 22, μ: 33735, ~: 33755) +StorageLensTest:test_extsload() (gas: 66705) +TickLensTest:test_GetPopulatedTicksInRange() (gas: 11486985) \ No newline at end of file diff --git a/test/foundry/PoolLens.t.sol b/test/foundry/PoolLens.t.sol index a526e80..be49df7 100644 --- a/test/foundry/PoolLens.t.sol +++ b/test/foundry/PoolLens.t.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "@pancakeswap/v3-core/contracts/interfaces/IPancakeV3Factory.sol"; import "contracts/EphemeralPoolSlots.sol"; import "contracts/EphemeralPoolTicks.sol"; import "contracts/EphemeralPoolTickBitmap.sol"; @@ -69,71 +68,3 @@ contract PoolLensTest is BaseTest, PoolUtils { } } } - -contract PCSV3PoolLensTest is BaseTest, PoolUtils { - function setUp() public override { - chainId = 56; - dex = DEX.PancakeSwapV3; - super.setUp(); - } - - function test_GetSlots() public { - try new EphemeralPCSV3PoolSlots(V3PoolCallee.wrap(pool)) {} catch (bytes memory returnData) { - Slot[] memory slots = abi.decode(returnData, (Slot[])); - uint256 length = slots.length; - for (uint256 i; i < length; ++i) { - assertEq(bytes32(slots[i].data), vm.load(pool, bytes32(slots[i].slot))); - } - } - } - - function test_GetPopulatedTicksInRange() public { - int24 tick = currentTick(); - try new EphemeralPCSV3PoolTicks(V3PoolCallee.wrap(pool), tick, tick) {} catch (bytes memory returnData) { - Slot[] memory slots = abi.decode(returnData, (Slot[])); - uint256 length = slots.length; - for (uint256 i; i < length; ++i) { - assertEq(bytes32(slots[i].data), vm.load(pool, bytes32(slots[i].slot))); - } - } - } - - function test_GetTickBitmap() public { - try new EphemeralPCSV3PoolTickBitmap(V3PoolCallee.wrap(pool)) {} catch (bytes memory returnData) { - Slot[] memory slots = abi.decode(returnData, (Slot[])); - uint256 length = slots.length; - for (uint256 i; i < length; ++i) { - assertEq(bytes32(slots[i].data), vm.load(pool, bytes32(slots[i].slot))); - } - } - } - - function test_GetPositions() public { - int24 tick = currentTick(); - testFuzz_GetPositions(tick - tickSpacing, tick + tickSpacing); - } - - /// forge-config: default.fuzz.runs = 16 - /// forge-config: ci.fuzz.runs = 16 - function testFuzz_GetPositions(int24 tickLower, int24 tickUpper) public { - (tickLower, tickUpper) = prepTicks(tickLower, tickUpper); - uint256 amount0Desired = token0Unit; - uint256 amount1Desired = token1Unit; - deal(token0, address(this), type(uint256).max); - deal(token1, address(this), type(uint256).max); - PositionKey[] memory keys = new PositionKey[](3); - for (uint256 i; i < 3; ++i) { - mint(address(this), amount0Desired, amount1Desired, tickLower, tickUpper); - keys[i] = PositionKey(address(this), tickLower, tickUpper); - tickLower -= tickSpacing; - tickUpper += tickSpacing; - } - try new EphemeralPCSV3PoolPositions(V3PoolCallee.wrap(pool), keys) {} catch (bytes memory returnData) { - Slot[] memory slots = abi.decode(returnData, (Slot[])); - uint256 length = slots.length; - for (uint256 i; i < length; ++i) { - assertEq(bytes32(slots[i].data), vm.load(pool, bytes32(slots[i].slot))); - } - } - } -} diff --git a/test/foundry/PositionLens.t.sol b/test/foundry/PositionLens.t.sol index c7d9966..7de1342 100644 --- a/test/foundry/PositionLens.t.sol +++ b/test/foundry/PositionLens.t.sol @@ -1,10 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {IPCSV3NonfungiblePositionManager} from "@aperture_finance/uni-v3-lib/src/interfaces/INonfungiblePositionManager.sol"; import {PoolAddress} from "@aperture_finance/uni-v3-lib/src/PoolAddress.sol"; -import {PoolAddressPancakeSwapV3} from "@aperture_finance/uni-v3-lib/src/PoolAddressPancakeSwapV3.sol"; -import {IPancakeV3Pool} from "@pancakeswap/v3-core/contracts/interfaces/IPancakeV3Pool.sol"; import "contracts/EphemeralAllPositionsByOwner.sol"; import "contracts/EphemeralGetPosition.sol"; import "contracts/EphemeralGetPositions.sol"; @@ -111,9 +108,9 @@ contract PositionLensTest is BaseTest { uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, - uint32 feeProtocol, + uint8 feeProtocol, bool unlocked - ) = IPancakeV3Pool(pool).slot0(); + ) = IUniswapV3Pool(pool).slot0(); assertEq(sqrtPriceX96, pos.slot0.sqrtPriceX96, "sqrtPriceX96"); assertEq(tick, pos.slot0.tick, "tick"); assertEq(observationIndex, pos.slot0.observationIndex, "observationIndex"); @@ -165,18 +162,3 @@ contract PositionLensTest is BaseTest { } } } - -contract PCSV3PositionLensTest is PositionLensTest { - function setUp() public override { - chainId = 56; - dex = DEX.PancakeSwapV3; - super.setUp(); - } - - // Trivially override so that the "forge-config" settings are applied. - /// forge-config: default.fuzz.runs = 16 - /// forge-config: ci.fuzz.runs = 16 - function testFuzz_GetPosition(uint256 tokenId) public override { - super.testFuzz_GetPosition(tokenId); - } -} diff --git a/test/foundry/StorageLens.t.sol b/test/foundry/StorageLens.t.sol index 1ca1628..db9f71d 100644 --- a/test/foundry/StorageLens.t.sol +++ b/test/foundry/StorageLens.t.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "@pancakeswap/v3-core/contracts/interfaces/IPancakeV3Factory.sol"; import "contracts/EphemeralStorageLens.sol"; import "./Base.t.sol"; @@ -32,11 +31,3 @@ contract StorageLensTest is BaseTest { assertEq(values[0], vm.load(pool, slot)); } } - -contract PCSV3StorageLensTest is StorageLensTest { - function setUp() public override { - chainId = 56; - dex = DEX.PancakeSwapV3; - super.setUp(); - } -} diff --git a/test/foundry/TickLens.t.sol b/test/foundry/TickLens.t.sol index 88923d7..e87c3ed 100644 --- a/test/foundry/TickLens.t.sol +++ b/test/foundry/TickLens.t.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "@pancakeswap/v3-core/contracts/interfaces/IPancakeV3Factory.sol"; import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; import "contracts/EphemeralGetPopulatedTicksInRange.sol"; import "./Base.t.sol"; @@ -42,11 +41,3 @@ contract TickLensTest is BaseTest, PoolUtils { } } } - -contract PCSV3TickLensTest is TickLensTest { - function setUp() public override { - chainId = 56; - dex = DEX.PancakeSwapV3; - super.setUp(); - } -} diff --git a/test/hardhat/pcsv3_test.ts b/test/hardhat/pcsv3_test.ts deleted file mode 100644 index 67f2e26..0000000 --- a/test/hardhat/pcsv3_test.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { TickMath } from "@uniswap/v3-sdk"; -import { expect } from "chai"; -import { ContractFunctionReturnType, createPublicClient, getContract, http, toHex } from "viem"; -import { bsc } from "viem/chains"; -import { - AutomatedMarketMakerEnum, - getAllPositionsByOwner, - getPopulatedTicksInRange, - getPositionDetails, - getPositions, - getPositionsSlots, - getStaticSlots, - getStorageAt, - getTickBitmapSlots, - getTicksSlots, -} from "../../src/viem"; -import { - EphemeralGetPositions__factory, - EphemeralPoolSlots__factory, - INonfungiblePositionManager__factory, - IPancakeV3Pool__factory, -} from "../../typechain"; -import { computePoolAddress } from "@pancakeswap/v3-sdk"; -import { Token } from "@pancakeswap/sdk"; - -const AMM = AutomatedMarketMakerEnum.enum.PANCAKESWAP_V3; -const PCSV3_NPM = "0x46A15B0b27311cedF172AB29E4f4766fbE7F4364"; -const PCSV3_POOL_DEPLOYER = "0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9"; -const USDT_ADDRESS = "0x55d398326f99059fF775485246999027B3197955"; -const WBNB_ADDRESS = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"; - -describe("Pool lens test with PCSV3 on BSC", () => { - const publicClient = createPublicClient({ - chain: bsc, - transport: http(`https://bsc-rpc.publicnode.com`), - batch: { - multicall: true, - }, - }); - var blockNumber: bigint; - const pool = computePoolAddress({ - deployerAddress: PCSV3_POOL_DEPLOYER, - tokenA: new Token(bsc.id, USDT_ADDRESS, 6, "USDT"), - tokenB: new Token(bsc.id, WBNB_ADDRESS, 18, "WBNB"), - fee: 500, - }); - const poolContract = getContract({ - address: pool, - abi: IPancakeV3Pool__factory.abi, - client: publicClient, - }); - const npm = getContract({ - address: PCSV3_NPM, - abi: INonfungiblePositionManager__factory.abi, - client: publicClient, - }); - - before(async () => { - blockNumber = (await publicClient.getBlockNumber()) - 64n; - console.log(`Running PCSV3 tests on the BNB chain at block number ${blockNumber}...`); - }); - - it("Test extsload", async () => { - const slots = await getStorageAt( - pool, - Array.from({ length: 4 }, (_, i) => toHex(i, { size: 32 })), - publicClient, - blockNumber, - ); - await Promise.all( - slots.map(async (slot, i) => { - const _slot = await publicClient.getStorageAt({ address: pool, slot: toHex(i), blockNumber }); - expect(slot).to.be.eq(_slot); - }), - ); - }); - - it("Test getting populated ticks", async () => { - const [, tickCurrent] = await poolContract.read.slot0({ blockNumber }); - const ticks = await getPopulatedTicksInRange(pool, tickCurrent, tickCurrent, publicClient, blockNumber); - await Promise.all( - ticks.map(async ({ tick, liquidityGross, liquidityNet }) => { - const [_liquidityGross, _liquidityNet] = await poolContract.read.ticks([tick], { blockNumber }); - expect(liquidityGross).to.be.eq(_liquidityGross); - expect(liquidityNet).to.be.eq(_liquidityNet); - }), - ); - }); - - it("Test getting position details", async () => { - const { - tokenId, - position: { token0, token1, fee }, - slot0: { sqrtPriceX96, tick }, - } = await getPositionDetails(PCSV3_NPM, 4n, publicClient, blockNumber); - expect(tokenId).to.be.eq(4n); - const poolAddress = computePoolAddress({ - deployerAddress: PCSV3_POOL_DEPLOYER, - tokenA: new Token(bsc.id, token0, 0, "NOT_USED"), - tokenB: new Token(bsc.id, token1, 0, "NOT_USED"), - fee, - }); - const [_sqrtPriceX96, _tick] = await getContract({ - address: poolAddress, - abi: IPancakeV3Pool__factory.abi, - client: publicClient, - }).read.slot0({ blockNumber }); - expect(sqrtPriceX96).to.be.eq(_sqrtPriceX96); - expect(tick).to.be.eq(_tick); - }); - - async function verifyPositionDetails(posArr: ContractFunctionReturnType) { - await Promise.all( - posArr.map(async ({ tokenId, position }) => { - const [, , token0, token1, fee, tickLower, tickUpper, liquidity] = await npm.read.positions([tokenId], { - blockNumber, - }); - expect(position.token0).to.be.eq(token0); - expect(position.token1).to.be.eq(token1); - expect(position.fee).to.be.eq(fee); - expect(position.tickLower).to.be.eq(tickLower); - expect(position.tickUpper).to.be.eq(tickUpper); - expect(position.liquidity).to.be.eq(liquidity); - }), - ); - } - - it("Test getting position array", async () => { - const posArr = await getPositions( - PCSV3_NPM, - Array.from({ length: 100 }, (_, i) => BigInt(i + 1)), - publicClient, - blockNumber, - ); - await verifyPositionDetails(posArr); - }); - - it("Test getting all positions by owner", async () => { - const totalSupply = await npm.read.totalSupply({ blockNumber }); - const tokenId = await npm.read.tokenByIndex([totalSupply - 1n], { blockNumber }); - const owner = await npm.read.ownerOf([tokenId], { blockNumber }); - const posArr = await getAllPositionsByOwner(PCSV3_NPM, owner, publicClient, blockNumber); - await verifyPositionDetails(posArr); - }); - - async function verifySlots(slots: ContractFunctionReturnType) { - expect(slots.some(({ data }) => data > 0)).to.be.true; - const address = pool; - const altSlots = await Promise.all( - slots.slice(0, 4).map(({ slot }) => publicClient.getStorageAt({ address, slot: toHex(slot), blockNumber })), - ); - for (let i = 0; i < altSlots.length; i++) { - expect(slots[i].data).to.be.eq(BigInt(altSlots[i]!)); - } - } - - it("Test getting static storage slots", async () => { - const slots = await getStaticSlots(AMM, pool, publicClient, blockNumber); - await verifySlots(slots); - }); - - it("Test getting populated ticks slots", async () => { - const slots = await getTicksSlots(AMM, pool, TickMath.MIN_TICK, TickMath.MAX_TICK, publicClient, blockNumber); - await verifySlots(slots); - }); - - it("Test getting tick bitmap slots", async () => { - const slots = await getTickBitmapSlots(AMM, pool, publicClient, blockNumber); - await verifySlots(slots); - }); - - it("Test getting positions mapping slots", async () => { - const logs = await poolContract.getEvents.Mint( - {}, - { - fromBlock: blockNumber - 10000n, - toBlock: blockNumber, - }, - ); - const positions = logs.map(({ args: { owner, tickLower, tickUpper } }) => ({ - owner: owner!, - tickLower: tickLower!, - tickUpper: tickUpper!, - })); - const slots = await getPositionsSlots(AMM, pool, positions, publicClient, blockNumber); - await verifySlots(slots); - }); -});