From 073ee0389ee8c60f64c89588fe26745fae501888 Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Sun, 1 Sep 2024 00:44:54 -0400 Subject: [PATCH] feat(alloy): update alloy to 0.3 (#6) * 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. * 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. * Fix test * 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 +++---- Cargo.toml | 10 +- README.md | 8 +- package.json | 3 +- src/lib.rs | 3 + src/pool_lens.rs | 82 ++++++++++++-- src/position_lens.rs | 46 +++++++- src/storage_lens.rs | 5 + 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 -------------------------------- 13 files changed, 155 insertions(+), 338 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/Cargo.toml b/Cargo.toml index 1043929..ac813eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,21 @@ [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." +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] -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/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 16e87a5..b6efbcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,12 @@ //! # uniswap-lens +//! +//! A library for querying Uniswap V3 using ephemeral lens contracts. #![warn( missing_copy_implementations, 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..021148d 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::{ @@ -18,17 +22,30 @@ use crate::{ use alloy::{ contract::Error, eips::BlockId, - primitives::{Address, Bytes}, + primitives::{aliases::I24, Address, Bytes}, providers::Provider, sol_types::SolCall, transports::{Transport, TransportError}, }; 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: i32, - tick_upper: i32, + tick_lower: I24, + tick_upper: I24, provider: P, block_id: Option, ) -> Result> @@ -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,10 +101,23 @@ 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: i32, - tick_upper: i32, + tick_lower: I24, + tick_upper: I24, provider: P, block_id: Option, ) -> Result> @@ -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, @@ -133,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), ) diff --git a/src/position_lens.rs b/src/position_lens.rs index 51cc3fc..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, @@ -94,7 +134,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 +159,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 +167,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) } 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, }; 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); - }); -});