From 0642c4e188d02eb96f9211c81c81f72dcfcbde96 Mon Sep 17 00:00:00 2001 From: gnarlycow Date: Sat, 27 Jul 2024 14:41:53 -0700 Subject: [PATCH] Finish support for SlipStream. --- contracts/Dex.sol | 8 + contracts/EphemeralAllPositionsByOwner.sol | 6 +- .../EphemeralGetPopulatedTicksInRange.sol | 38 +- contracts/EphemeralPoolPositions.sol | 29 +- contracts/EphemeralPoolSlots.sol | 38 +- contracts/EphemeralPoolTickBitmap.sol | 25 +- contracts/EphemeralPoolTicks.sol | 30 +- contracts/PoolUtils.sol | 45 +- contracts/PositionLens.sol | 8 +- contracts/PositionUtils.sol | 34 +- contracts/interfaces/ISlipStreamCLFactory.sol | 14 + contracts/interfaces/ISlipStreamCLPool.sol | 78 + package.json | 4 +- src/viem/amm.ts | 21 + src/viem/poolLens.ts | 94 +- src/viem/positionLens.ts | 15 +- test/foundry/Base.t.sol | 7 +- test/foundry/PoolLens.t.sol | 71 +- test/foundry/PositionLens.t.sol | 92 +- test/foundry/StorageLens.t.sol | 23 +- test/foundry/TickLens.t.sol | 38 +- test/hardhat/SlipStreamPool_abi.json | 1309 ----------------- test/hardhat/pcsv3_test.ts | 4 +- test/hardhat/slipstream_test.ts | 59 +- test/hardhat/univ3_test.ts | 4 +- yarn.lock | 199 +-- 26 files changed, 514 insertions(+), 1779 deletions(-) create mode 100644 contracts/Dex.sol create mode 100644 contracts/interfaces/ISlipStreamCLFactory.sol create mode 100644 contracts/interfaces/ISlipStreamCLPool.sol create mode 100644 src/viem/amm.ts delete mode 100644 test/hardhat/SlipStreamPool_abi.json diff --git a/contracts/Dex.sol b/contracts/Dex.sol new file mode 100644 index 0000000..5983165 --- /dev/null +++ b/contracts/Dex.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +enum DEX { + UniswapV3, + PancakeSwapV3, + SlipStream +} diff --git a/contracts/EphemeralAllPositionsByOwner.sol b/contracts/EphemeralAllPositionsByOwner.sol index 7321f81..02e23be 100644 --- a/contracts/EphemeralAllPositionsByOwner.sol +++ b/contracts/EphemeralAllPositionsByOwner.sol @@ -22,7 +22,11 @@ contract EphemeralAllPositionsByOwner is PositionUtils { /// @param dex DEX /// @param npm Nonfungible position manager /// @param owner The address that owns the NFTs - function allPositions(DEX dex, address npm, address owner) public payable returns (PositionState[] memory positions) { + function allPositions( + DEX dex, + address npm, + address owner + ) public payable returns (PositionState[] memory positions) { uint256 balance = NPMCaller.balanceOf(INPM(npm), owner); positions = new PositionState[](balance); unchecked { diff --git a/contracts/EphemeralGetPopulatedTicksInRange.sol b/contracts/EphemeralGetPopulatedTicksInRange.sol index d7b8bda..f398dc0 100644 --- a/contracts/EphemeralGetPopulatedTicksInRange.sol +++ b/contracts/EphemeralGetPopulatedTicksInRange.sol @@ -1,15 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; +import {DEX} from "./Dex.sol"; import "./PoolUtils.sol"; +import "./interfaces/ISlipStreamCLPool.sol"; /// @notice A lens that fetches chunks of tick data in a range for a Uniswap v3 pool without deployment /// @author Aperture Finance /// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the /// revert data, and decoded by `abi.decode(data, (PopulatedTick[]))` contract EphemeralGetPopulatedTicksInRange is PoolUtils { - constructor(V3PoolCallee pool, int24 tickLower, int24 tickUpper) payable { - PopulatedTick[] memory populatedTicks = getPopulatedTicksInRange(pool, tickLower, tickUpper); + constructor(DEX dex, V3PoolCallee pool, int24 tickLower, int24 tickUpper) payable { + PopulatedTick[] memory populatedTicks = getPopulatedTicksInRange(dex, pool, tickLower, tickUpper); bytes memory returnData = abi.encode(populatedTicks); assembly ("memory-safe") { revert(add(returnData, 0x20), mload(returnData)) @@ -22,6 +24,7 @@ contract EphemeralGetPopulatedTicksInRange is PoolUtils { /// @param tickUpper The upper tick boundary of the populated ticks to fetch /// @return populatedTicks An array of tick data for the given word in the tick bitmap function getPopulatedTicksInRange( + DEX dex, V3PoolCallee pool, int24 tickLower, int24 tickUpper @@ -37,6 +40,7 @@ contract EphemeralGetPopulatedTicksInRange is PoolUtils { uint256 idx; for (int16 wordPos = wordPosLower; wordPos <= wordPosUpper; ++wordPos) { idx = populateTicksInWord( + dex, pool, wordPos, tickSpacing, @@ -50,6 +54,7 @@ contract EphemeralGetPopulatedTicksInRange is PoolUtils { /// @notice Get the tick data for all populated ticks in a word of the tick bitmap function populateTicksInWord( + DEX dex, V3PoolCallee pool, int16 wordPos, int24 tickSpacing, @@ -65,19 +70,34 @@ contract EphemeralGetPopulatedTicksInRange is PoolUtils { assembly { tick := mul(tickSpacing, add(shl(8, wordPos), bitPos)) } - populateTick(pool, tick, populatedTicks[idx++]); + populateTick(dex, pool, tick, populatedTicks[idx++]); } } return idx; } } - function populateTick(V3PoolCallee pool, int24 tick, PopulatedTick memory populatedTick) internal view { - PoolCaller.TickInfo memory info = pool.ticks(tick); + function populateTick(DEX dex, V3PoolCallee pool, int24 tick, PopulatedTick memory populatedTick) internal view { populatedTick.tick = tick; - populatedTick.liquidityNet = info.liquidityNet; - populatedTick.liquidityGross = info.liquidityGross; - populatedTick.feeGrowthOutside0X128 = info.feeGrowthOutside0X128; - populatedTick.feeGrowthOutside1X128 = info.feeGrowthOutside1X128; + if (dex == DEX.SlipStream) { + ( + populatedTick.liquidityGross, + populatedTick.liquidityNet, + , + populatedTick.feeGrowthOutside0X128, + populatedTick.feeGrowthOutside1X128, + , + , + , + , + + ) = ISlipStreamCLPool(V3PoolCallee.unwrap(pool)).ticks(tick); + } else { + PoolCaller.TickInfo memory info = pool.ticks(tick); + populatedTick.liquidityNet = info.liquidityNet; + populatedTick.liquidityGross = info.liquidityGross; + populatedTick.feeGrowthOutside0X128 = info.feeGrowthOutside0X128; + populatedTick.feeGrowthOutside1X128 = info.feeGrowthOutside1X128; + } } } diff --git a/contracts/EphemeralPoolPositions.sol b/contracts/EphemeralPoolPositions.sol index da9a7b7..8d6dff5 100644 --- a/contracts/EphemeralPoolPositions.sol +++ b/contracts/EphemeralPoolPositions.sol @@ -2,23 +2,25 @@ pragma solidity ^0.8.0; import "./PoolUtils.sol"; +import {DEX} from "./Dex.sol"; /// @notice A lens that batches fetching of the `positions` mapping for a Uniswap v3 pool without deployment /// @author Aperture Finance /// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the /// revert data, and decoded by `abi.decode(data, (Slot[]))` contract EphemeralPoolPositions is PoolUtils { - constructor(V3PoolCallee pool, PositionKey[] memory keys) payable { - Slot[] memory slots = getPositions(pool, keys); + constructor(DEX dex, V3PoolCallee pool, PositionKey[] memory keys) payable { + Slot[] memory slots = getPositions(dex, pool, keys); bytes memory returnData = abi.encode(slots); assembly ("memory-safe") { revert(add(returnData, 0x20), mload(returnData)) } } - function getPositionsSlot() internal pure virtual returns (uint256) { - // Storage slot of the `positions` mapping in UniswapV3Pool. - return 7; + function getPositionsSlot(DEX dex) internal pure virtual returns (uint256) { + if (dex == DEX.UniswapV3) return 7; + if (dex == DEX.PancakeSwapV3) return 8; + revert("EphemeralPoolPositions: invalid or unsupported DEX"); } /// @notice Get liquidity positions in a pool @@ -26,9 +28,13 @@ contract EphemeralPoolPositions is PoolUtils { /// @param pool The address of the pool for which to fetch the tick bitmap /// @param keys The position keys to fetch /// @return slots An array of storage slots and their raw data - function getPositions(V3PoolCallee pool, PositionKey[] memory keys) public payable returns (Slot[] memory slots) { + function getPositions( + DEX dex, + V3PoolCallee pool, + PositionKey[] memory keys + ) public payable returns (Slot[] memory slots) { unchecked { - uint256 POSITIONS_SLOT = getPositionsSlot(); + uint256 POSITIONS_SLOT = getPositionsSlot(dex); uint256 length = keys.length; // each position occupies 4 storage slots slots = new Slot[](length << 2); @@ -56,12 +62,3 @@ contract EphemeralPoolPositions is PoolUtils { } } } - -contract EphemeralPCSV3PoolPositions is EphemeralPoolPositions { - constructor(V3PoolCallee pool, PositionKey[] memory keys) payable EphemeralPoolPositions(pool, keys) {} - - function getPositionsSlot() internal pure override returns (uint256) { - // Storage slot of the `positions` mapping in PancakeSwapV3Pool. - return 8; - } -} diff --git a/contracts/EphemeralPoolSlots.sol b/contracts/EphemeralPoolSlots.sol index 1395b8f..1307950 100644 --- a/contracts/EphemeralPoolSlots.sol +++ b/contracts/EphemeralPoolSlots.sol @@ -3,14 +3,15 @@ pragma solidity ^0.8.0; import "@pancakeswap/v3-core/contracts/interfaces/IPancakeV3Pool.sol"; import "./PoolUtils.sol"; +import {DEX} from "./Dex.sol"; /// @notice A lens for fetching static state variables in a Uniswap v3 pool without deployment /// @author Aperture Finance /// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the /// revert data, and decoded by `abi.decode(data, (Slot[]))` contract EphemeralPoolSlots is PoolUtils { - constructor(V3PoolCallee pool) payable { - Slot[] memory slots = getSlots(pool); + constructor(DEX dex, V3PoolCallee pool) payable { + Slot[] memory slots = getSlots(dex, pool); bytes memory returnData = abi.encode(slots); assembly ("memory-safe") { revert(add(returnData, 0x20), mload(returnData)) @@ -21,7 +22,20 @@ contract EphemeralPoolSlots is PoolUtils { /// @dev Public function to expose the abi for easier decoding using TypeChain /// @param pool The Uniswap v3 pool /// @return slots An array of storage slots and their raw data - function getSlots(V3PoolCallee pool) public payable returns (Slot[] memory slots) { + function getSlots(DEX dex, V3PoolCallee pool) public payable returns (Slot[] memory slots) { + if (dex == DEX.UniswapV3) { + return getUniV3PoolSlots(pool); + } else if (dex == DEX.PancakeSwapV3) { + return getPCSV3PoolSlots(pool); + } + revert("EphemeralPoolSlots: invalid or unsupported DEX"); + } + + /// @notice Get the static storage slots of a Uniswap v3 pool + /// @dev Public function to expose the abi for easier decoding using TypeChain + /// @param pool The Uniswap v3 pool + /// @return slots An array of storage slots and their raw data + function getUniV3PoolSlots(V3PoolCallee pool) internal view returns (Slot[] memory slots) { unchecked { uint256 length; { @@ -78,26 +92,12 @@ contract EphemeralPoolSlots is PoolUtils { } } } -} -/// @notice A lens for fetching static state variables in a PancakeSwap v3 pool without deployment -/// @author Aperture Finance -/// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the -/// revert data, and decoded by `abi.decode(data, (Slot[]))` -contract EphemeralPCSV3PoolSlots is PoolUtils { - constructor(V3PoolCallee pool) payable { - Slot[] memory slots = getSlots(pool); - bytes memory returnData = abi.encode(slots); - assembly ("memory-safe") { - revert(add(returnData, 0x20), mload(returnData)) - } - } - - /// @notice Get the static storage slots of a pool + /// @notice Get the static storage slots of a PancakeSwap v3 pool /// @dev Public function to expose the abi for easier decoding using TypeChain /// @param pool The PancakeSwap v3 pool /// @return slots An array of storage slots and their raw data - function getSlots(V3PoolCallee pool) public payable returns (Slot[] memory slots) { + function getPCSV3PoolSlots(V3PoolCallee pool) internal view returns (Slot[] memory slots) { unchecked { uint256 length; { diff --git a/contracts/EphemeralPoolTickBitmap.sol b/contracts/EphemeralPoolTickBitmap.sol index 637f91b..03dd86b 100644 --- a/contracts/EphemeralPoolTickBitmap.sol +++ b/contracts/EphemeralPoolTickBitmap.sol @@ -3,34 +3,36 @@ pragma solidity ^0.8.0; import {TickMath} from "@aperture_finance/uni-v3-lib/src/TickMath.sol"; import "./PoolUtils.sol"; +import {DEX} from "./Dex.sol"; /// @notice A lens that fetches the `tickBitmap` for a Uniswap v3 pool without deployment /// @author Aperture Finance /// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the /// revert data, and decoded by `abi.decode(data, (Slot[]))` contract EphemeralPoolTickBitmap is PoolUtils { - constructor(V3PoolCallee pool) payable { - Slot[] memory slots = getTickBitmap(pool); + constructor(DEX dex, V3PoolCallee pool) payable { + Slot[] memory slots = getTickBitmap(dex, pool); bytes memory returnData = abi.encode(slots); assembly ("memory-safe") { revert(add(returnData, 0x20), mload(returnData)) } } - function getTickBitmapSlot() internal pure virtual returns (uint256) { - // Storage slot of the `tickBitmap` mapping in UniswapV3Pool. - return 6; + function getTickBitmapSlot(DEX dex) internal pure virtual returns (uint256) { + if (dex == DEX.UniswapV3) return 6; + if (dex == DEX.PancakeSwapV3) return 7; + revert("EphemeralPoolTickBitmap: invalid or unsupported DEX"); } /// @notice Get the tick bitmap for a pool /// @dev Public function to expose the abi for easier decoding using TypeChain /// @param pool The address of the pool for which to fetch the tick bitmap /// @return slots An array of storage slots and their raw data - function getTickBitmap(V3PoolCallee pool) public payable returns (Slot[] memory slots) { + function getTickBitmap(DEX dex, V3PoolCallee pool) public payable returns (Slot[] memory slots) { // checks that the pool exists int24 tickSpacing = IUniswapV3Pool(V3PoolCallee.unwrap(pool)).tickSpacing(); (int16 wordPosLower, int16 wordPosUpper) = getWordPositions(TickMath.MIN_TICK, TickMath.MAX_TICK, tickSpacing); - uint256 TICKBITMAP_SLOT = getTickBitmapSlot(); + uint256 TICKBITMAP_SLOT = getTickBitmapSlot(dex); unchecked { slots = new Slot[](uint16(wordPosUpper - wordPosLower + 1)); for (int16 wordPos = wordPosLower; wordPos <= wordPosUpper; ++wordPos) { @@ -47,12 +49,3 @@ contract EphemeralPoolTickBitmap is PoolUtils { } } } - -contract EphemeralPCSV3PoolTickBitmap is EphemeralPoolTickBitmap { - constructor(V3PoolCallee pool) payable EphemeralPoolTickBitmap(pool) {} - - function getTickBitmapSlot() internal pure override returns (uint256) { - // Storage slot of the `tickBitmap` mapping in PancakeSwapV3Pool. - return 7; - } -} diff --git a/contracts/EphemeralPoolTicks.sol b/contracts/EphemeralPoolTicks.sol index 42ac349..7ad7c54 100644 --- a/contracts/EphemeralPoolTicks.sol +++ b/contracts/EphemeralPoolTicks.sol @@ -2,23 +2,25 @@ pragma solidity ^0.8.0; import "./PoolUtils.sol"; +import {DEX} from "./Dex.sol"; /// @notice A lens that fetches raw storage slots of the `ticks` mapping in a range for a Uniswap v3 pool without deployment /// @author Aperture Finance /// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the /// revert data, and decoded by `abi.decode(data, (Slot[]))` contract EphemeralPoolTicks is PoolUtils { - constructor(V3PoolCallee pool, int24 tickLower, int24 tickUpper) payable { - Slot[] memory slots = getPopulatedTicksInRange(pool, tickLower, tickUpper); + constructor(DEX dex, V3PoolCallee pool, int24 tickLower, int24 tickUpper) payable { + Slot[] memory slots = getPopulatedTicksInRange(dex, pool, tickLower, tickUpper); bytes memory returnData = abi.encode(slots); assembly ("memory-safe") { revert(add(returnData, 0x20), mload(returnData)) } } - function getTicksSlot() internal pure virtual returns (uint256) { - // Storage slot of the `ticks` mapping in UniswapV3Pool. - return 5; + function getTicksSlot(DEX dex) internal pure virtual returns (uint256) { + if (dex == DEX.UniswapV3) return 5; + if (dex == DEX.PancakeSwapV3) return 6; + revert("EphemeralPoolTicks: invalid or unsupported DEX"); } /// @notice Get all the tick data for the populated ticks from tickLower to tickUpper @@ -28,6 +30,7 @@ contract EphemeralPoolTicks is PoolUtils { /// @param tickUpper The upper tick boundary of the populated ticks to fetch /// @return slots An array of storage slots and their raw data function getPopulatedTicksInRange( + DEX dex, V3PoolCallee pool, int24 tickLower, int24 tickUpper @@ -44,6 +47,7 @@ contract EphemeralPoolTicks is PoolUtils { uint256 idx; for (int16 wordPos = wordPosLower; wordPos <= wordPosUpper; ++wordPos) { idx = populateTicksInWord( + dex, pool, wordPos, tickSpacing, @@ -57,6 +61,7 @@ contract EphemeralPoolTicks is PoolUtils { /// @notice Get the tick data for all populated ticks in a word of the tick bitmap function populateTicksInWord( + DEX dex, V3PoolCallee pool, int16 wordPos, int24 tickSpacing, @@ -65,7 +70,7 @@ contract EphemeralPoolTicks is PoolUtils { uint256 idx ) internal view returns (uint256) { unchecked { - uint256 TICKS_SLOT = getTicksSlot(); + uint256 TICKS_SLOT = getTicksSlot(dex); for (uint256 bitPos; bitPos < 256; ++bitPos) { //slither-disable-next-line incorrect-shift if (bitmap & (1 << bitPos) != 0) { @@ -113,16 +118,3 @@ contract EphemeralPoolTicks is PoolUtils { } } } - -contract EphemeralPCSV3PoolTicks is EphemeralPoolTicks { - constructor( - V3PoolCallee pool, - int24 tickLower, - int24 tickUpper - ) payable EphemeralPoolTicks(pool, tickLower, tickUpper) {} - - function getTicksSlot() internal pure override returns (uint256) { - // Storage slot of the `ticks` mapping in PancakeSwapV3Pool. - return 6; - } -} diff --git a/contracts/PoolUtils.sol b/contracts/PoolUtils.sol index fb1c739..ea7306c 100644 --- a/contracts/PoolUtils.sol +++ b/contracts/PoolUtils.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; +import {DEX} from "./Dex.sol"; +import {ISlipStreamCLPool} from "./interfaces/ISlipStreamCLPool.sol"; import {FullMath} from "@aperture_finance/uni-v3-lib/src/FullMath.sol"; import {IUniswapV3Pool, PoolCaller, V3PoolCallee} from "@aperture_finance/uni-v3-lib/src/PoolCaller.sol"; import {TickBitmap} from "@aperture_finance/uni-v3-lib/src/TickBitmap.sol"; @@ -71,30 +73,45 @@ abstract contract PoolUtils { /// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries /// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries function getFeeGrowthInside( - V3PoolCallee pool, + DEX dex, + address pool, int24 tickLower, int24 tickUpper, int24 tickCurrent ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { - PoolCaller.TickInfo memory lower = pool.ticks(tickLower); - PoolCaller.TickInfo memory upper = pool.ticks(tickUpper); - + uint256 tickLowerFeeGrowthOutside0X128; + uint256 tickLowerFeeGrowthOutside1X128; + uint256 tickUpperFeeGrowthOutside0X128; + uint256 tickUpperFeeGrowthOutside1X128; + if (dex == DEX.SlipStream) { + (, , , tickLowerFeeGrowthOutside0X128, tickLowerFeeGrowthOutside1X128, , , , , ) = ISlipStreamCLPool(pool) + .ticks(tickLower); + (, , , tickUpperFeeGrowthOutside0X128, tickUpperFeeGrowthOutside1X128, , , , , ) = ISlipStreamCLPool(pool) + .ticks(tickUpper); + } else { + (, , tickLowerFeeGrowthOutside0X128, tickLowerFeeGrowthOutside1X128, , , , ) = IUniswapV3Pool(pool).ticks( + tickLower + ); + (, , tickUpperFeeGrowthOutside0X128, tickUpperFeeGrowthOutside1X128, , , , ) = IUniswapV3Pool(pool).ticks( + tickUpper + ); + } unchecked { if (tickCurrent < tickLower) { - feeGrowthInside0X128 = lower.feeGrowthOutside0X128 - upper.feeGrowthOutside0X128; - feeGrowthInside1X128 = lower.feeGrowthOutside1X128 - upper.feeGrowthOutside1X128; + feeGrowthInside0X128 = tickLowerFeeGrowthOutside0X128 - tickUpperFeeGrowthOutside0X128; + feeGrowthInside1X128 = tickLowerFeeGrowthOutside1X128 - tickUpperFeeGrowthOutside1X128; } else if (tickCurrent >= tickUpper) { - feeGrowthInside0X128 = upper.feeGrowthOutside0X128 - lower.feeGrowthOutside0X128; - feeGrowthInside1X128 = upper.feeGrowthOutside1X128 - lower.feeGrowthOutside1X128; + feeGrowthInside0X128 = tickUpperFeeGrowthOutside0X128 - tickLowerFeeGrowthOutside0X128; + feeGrowthInside1X128 = tickUpperFeeGrowthOutside1X128 - tickLowerFeeGrowthOutside1X128; } else { feeGrowthInside0X128 = - pool.feeGrowthGlobal0X128() - - lower.feeGrowthOutside0X128 - - upper.feeGrowthOutside0X128; + V3PoolCallee.wrap(pool).feeGrowthGlobal0X128() - + tickLowerFeeGrowthOutside0X128 - + tickUpperFeeGrowthOutside0X128; feeGrowthInside1X128 = - pool.feeGrowthGlobal1X128() - - lower.feeGrowthOutside1X128 - - upper.feeGrowthOutside1X128; + V3PoolCallee.wrap(pool).feeGrowthGlobal1X128() - + tickLowerFeeGrowthOutside1X128 - + tickUpperFeeGrowthOutside1X128; } } } diff --git a/contracts/PositionLens.sol b/contracts/PositionLens.sol index 1c051b7..3119d3c 100644 --- a/contracts/PositionLens.sol +++ b/contracts/PositionLens.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; +import {DEX} from "./Dex.sol"; import {LiquidityAmounts} from "@aperture_finance/uni-v3-lib/src/LiquidityAmounts.sol"; import {PoolCaller, V3PoolCallee} from "@aperture_finance/uni-v3-lib/src/PoolCaller.sol"; import {TickMath} from "@aperture_finance/uni-v3-lib/src/TickMath.sol"; @@ -20,6 +21,7 @@ contract PositionLens is PoolUtils { /// @return tokensOwed0 The amount of token0 owed to the position /// @return tokensOwed1 The amount of token1 owed to the position function getFeesOwed( + DEX dex, V3PoolCallee pool, address owner, int24 tickLower, @@ -31,7 +33,8 @@ contract PositionLens is PoolUtils { if (info.liquidity != 0) { (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = getFeeGrowthInside( - pool, + dex, + V3PoolCallee.unwrap(pool), tickLower, tickUpper, tickCurrent @@ -54,6 +57,7 @@ contract PositionLens is PoolUtils { /// @return amount0 The total amount of token0 held in the position /// @return amount1 The total amount of token1 held in the position function getTotalAmounts( + DEX dex, V3PoolCallee pool, address owner, int24 tickLower, @@ -70,7 +74,7 @@ contract PositionLens is PoolUtils { tickUpper.getSqrtRatioAtTick(), info.liquidity ); - (uint256 fees0, uint256 fees1) = getFeesOwed(pool, owner, tickLower, tickUpper); + (uint256 fees0, uint256 fees1) = getFeesOwed(dex, pool, owner, tickLower, tickUpper); amount0 += fees0; amount1 += fees1; } diff --git a/contracts/PositionUtils.sol b/contracts/PositionUtils.sol index 7a51ccb..1115bb0 100644 --- a/contracts/PositionUtils.sol +++ b/contracts/PositionUtils.sol @@ -11,12 +11,8 @@ import {IUniswapV3Factory} from "@uniswap/v3-core/contracts/interfaces/IUniswapV import {IPancakeV3Factory} from "@pancakeswap/v3-core/contracts/interfaces/IPancakeV3Factory.sol"; import {ERC20Callee} from "./libraries/ERC20Caller.sol"; import {PoolUtils} from "./PoolUtils.sol"; - -enum DEX { - UniswapV3, - PancakeSwapV3, - SlipStream -} +import {DEX} from "./Dex.sol"; +import "./interfaces/ISlipStreamCLFactory.sol"; struct Slot0 { uint160 sqrtPriceX96; @@ -76,18 +72,6 @@ struct PositionState { uint8 decimals1; } -// Partial interface for the SlipStream factory. SlipStream factory is named "CLFactory" and "CL" presumably stands for concentrated liquidity. -// https://github.com/velodrome-finance/slipstream/blob/main/contracts/core/interfaces/ICLFactory.sol -interface ISlipStreamCLFactory { - /// @notice Returns the pool address for a given pair of tokens and a tick spacing, or address 0 if it does not exist - /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order - /// @param tokenA The contract address of either token0 or token1 - /// @param tokenB The contract address of the other token - /// @param tickSpacing The tick spacing of the pool - /// @return pool The pool address - function getPool(address tokenA, address tokenB, int24 tickSpacing) external view returns (address pool); -} - /// @title Position utility contract /// @author Aperture Finance /// @notice Base contract for Uniswap v3 that peeks into the current state of position and pool info @@ -128,9 +112,15 @@ abstract contract PositionUtils is PoolUtils { state.poolTickSpacing = pool.tickSpacing(); state.activeLiquidity = pool.liquidity(); slot0InPlace(pool, state.slot0); + // Manually adjust `feeProtocol` and `unlocked` fields for SlipStream as `feeProtocol` doesn't exist on SlipStream. + if (dex == DEX.SlipStream) { + state.slot0.feeProtocol = 0; + state.slot0.unlocked = true; + } if (position.liquidity != 0) { (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = getFeeGrowthInside( - pool, + dex, + state.pool, position.tickLower, position.tickUpper, state.slot0.tick @@ -154,7 +144,11 @@ abstract contract PositionUtils is PoolUtils { /// @param tokenId The ID of the token that represents the position /// @param pos The position pointer to be updated in place /// @return exists Whether the position exists - function positionInPlace(address npm, uint256 tokenId, PositionFull memory pos) internal view returns (bool exists) { + function positionInPlace( + address npm, + uint256 tokenId, + PositionFull memory pos + ) internal view returns (bool exists) { bytes4 selector = IUniV3NPM(npm).positions.selector; assembly ("memory-safe") { // Write the abi-encoded calldata into memory. diff --git a/contracts/interfaces/ISlipStreamCLFactory.sol b/contracts/interfaces/ISlipStreamCLFactory.sol new file mode 100644 index 0000000..9f8a1d9 --- /dev/null +++ b/contracts/interfaces/ISlipStreamCLFactory.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +// Partial interface for the SlipStream factory. SlipStream factory is named "CLFactory" and "CL" presumably stands for concentrated liquidity. +// https://github.com/velodrome-finance/slipstream/blob/main/contracts/core/interfaces/ICLFactory.sol +interface ISlipStreamCLFactory { + /// @notice Returns the pool address for a given pair of tokens and a tick spacing, or address 0 if it does not exist + /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order + /// @param tokenA The contract address of either token0 or token1 + /// @param tokenB The contract address of the other token + /// @param tickSpacing The tick spacing of the pool + /// @return pool The pool address + function getPool(address tokenA, address tokenB, int24 tickSpacing) external view returns (address pool); +} diff --git a/contracts/interfaces/ISlipStreamCLPool.sol b/contracts/interfaces/ISlipStreamCLPool.sol new file mode 100644 index 0000000..58af9ef --- /dev/null +++ b/contracts/interfaces/ISlipStreamCLPool.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +interface ISlipStreamCLPool { + /// @notice Look up information about a specific tick in the pool + /// @param tick The tick to look up + /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or + /// tick upper, + /// liquidityNet how much liquidity changes when the pool price crosses the tick, + /// stakedLiquidityNet how much staked liquidity changes when the pool price crosses the tick, + /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, + /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, + /// rewardGrowthOutsideX128 the reward growth on the other side of the tick from the current tick in emission token + /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick + /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, + /// secondsOutside the seconds spent on the other side of the tick from the current tick, + /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false. + /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0. + /// In addition, these values are only relative and must be used only in comparison to previous snapshots for + /// a specific position. + function ticks( + int24 tick + ) + external + view + returns ( + uint128 liquidityGross, + int128 liquidityNet, + int128 stakedLiquidityNet, + uint256 feeGrowthOutside0X128, + uint256 feeGrowthOutside1X128, + uint256 rewardGrowthOutsideX128, + int56 tickCumulativeOutside, + uint160 secondsPerLiquidityOutsideX128, + uint32 secondsOutside, + bool initialized + ); + + /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas + /// when accessed externally. + /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value + /// tick The current tick of the pool, i.e. according to the last tick transition that was run. + /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick + /// boundary. + /// observationIndex The index of the last oracle observation that was written, + /// observationCardinality The current maximum number of observations stored in the pool, + /// observationCardinalityNext The next maximum number of observations, to be updated when the observation. + /// unlocked Whether the pool is currently locked to reentrancy + function slot0() + external + view + returns ( + uint160 sqrtPriceX96, + int24 tick, + uint16 observationIndex, + uint16 observationCardinality, + uint16 observationCardinalityNext, + bool unlocked + ); + + /// @notice Emitted when liquidity is minted for a given position + /// @param sender The address that minted the liquidity + /// @param owner The owner of the position and recipient of any minted liquidity + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount The amount of liquidity minted to the position range + /// @param amount0 How much token0 was required for the minted liquidity + /// @param amount1 How much token1 was required for the minted liquidity + event Mint( + address sender, + address indexed owner, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); +} diff --git a/package.json b/package.json index 7d766c3..0410451 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "devDependencies": { "@ethersproject/abi": "5.7.0", "@ethersproject/providers": "5.7.2", - "@nomicfoundation/hardhat-foundry": "^1.1.1", + "@nomicfoundation/hardhat-foundry": "^1.1.2", "@nomiclabs/hardhat-ethers": "^2.2.3", "@pancakeswap/v3-sdk": "^3.8.0", "@typechain/ethers-v5": "^11.1.2", @@ -67,7 +67,7 @@ "@uniswap/v3-sdk": "^3.11.0", "chai": "^4.4.1", "dotenv": "^16.4.5", - "hardhat": "^2.22.2", + "hardhat": "^2.22.6", "mocha": "^10.4.0", "prettier": "^3.2.5", "prettier-plugin-solidity": "^1.3.1", diff --git a/src/viem/amm.ts b/src/viem/amm.ts new file mode 100644 index 0000000..e5e993d --- /dev/null +++ b/src/viem/amm.ts @@ -0,0 +1,21 @@ +import { z } from "zod"; + +export const AutomatedMarketMakerEnum = z.enum([ + 'UNISWAP_V3', + 'PANCAKESWAP_V3', + 'SLIPSTREAM', +]); +export type AutomatedMarketMakerEnum = z.infer; + +export function ammToSolidityDexEnum(amm: AutomatedMarketMakerEnum): number { + if (amm === AutomatedMarketMakerEnum.enum.UNISWAP_V3) { + return 0; + } + if (amm === AutomatedMarketMakerEnum.enum.PANCAKESWAP_V3) { + return 1; + } + if (amm === AutomatedMarketMakerEnum.enum.SLIPSTREAM) { + return 2; + } + throw new Error(`Unexpected AMM: ${amm}`); +} diff --git a/src/viem/poolLens.ts b/src/viem/poolLens.ts index 7057b38..e79d54f 100644 --- a/src/viem/poolLens.ts +++ b/src/viem/poolLens.ts @@ -2,24 +2,13 @@ import { AbiParametersToPrimitiveTypes, ExtractAbiFunction } from "abitype"; import { Address, PublicClient } from "viem"; import { EphemeralGetPopulatedTicksInRange__factory, - EphemeralPCSV3PoolPositions__factory, - EphemeralPCSV3PoolSlots__factory, - EphemeralPCSV3PoolTickBitmap__factory, - EphemeralPCSV3PoolTicks__factory, EphemeralPoolPositions__factory, EphemeralPoolSlots__factory, EphemeralPoolTickBitmap__factory, EphemeralPoolTicks__factory, } from "../../typechain"; import { callEphemeralContract } from "./caller"; -import { z } from 'zod'; - -export const AutomatedMarketMakerEnum = z.enum([ - 'UNISWAP_V3', - 'PANCAKESWAP_V3', - 'SLIPSTREAM', -]); -export type AutomatedMarketMakerEnum = z.infer; +import { ammToSolidityDexEnum, AutomatedMarketMakerEnum } from "./amm"; /** * Fetches the liquidity within the tick range for the specified pool by deploying an ephemeral contract via `eth_call`. @@ -32,6 +21,7 @@ export type AutomatedMarketMakerEnum = z.infer; * @param blockNumber Optional block number to query. */ export async function getPopulatedTicksInRange( + amm: AutomatedMarketMakerEnum, pool: Address, tickLower: number, tickUpper: number, @@ -42,7 +32,7 @@ export async function getPopulatedTicksInRange( { abi: EphemeralGetPopulatedTicksInRange__factory.abi, bytecode: EphemeralGetPopulatedTicksInRange__factory.bytecode, - args: [pool, tickLower, tickUpper], + args: [ammToSolidityDexEnum(amm), pool, tickLower, tickUpper], }, publicClient, blockNumber, @@ -55,18 +45,15 @@ export async function getStaticSlots( publicClient: PublicClient, blockNumber?: bigint, ) { + if (amm === AutomatedMarketMakerEnum.enum.SLIPSTREAM) { + throw new Error("Not yet implemented for SLIPSTREAM"); + } return await callEphemeralContract( - amm === AutomatedMarketMakerEnum.enum.PANCAKESWAP_V3 - ? { - abi: EphemeralPCSV3PoolSlots__factory.abi, - bytecode: EphemeralPCSV3PoolSlots__factory.bytecode, - args: [pool], - } - : { - abi: EphemeralPoolSlots__factory.abi, - bytecode: EphemeralPoolSlots__factory.bytecode, - args: [pool], - }, + { + abi: EphemeralPoolSlots__factory.abi, + bytecode: EphemeralPoolSlots__factory.bytecode, + args: [ammToSolidityDexEnum(amm), pool], + }, publicClient, blockNumber, ); @@ -80,18 +67,15 @@ export async function getTicksSlots( publicClient: PublicClient, blockNumber?: bigint, ) { + if (amm === AutomatedMarketMakerEnum.enum.SLIPSTREAM) { + throw new Error("Not yet implemented for SLIPSTREAM"); + } return await callEphemeralContract( - amm === AutomatedMarketMakerEnum.enum.PANCAKESWAP_V3 - ? { - abi: EphemeralPCSV3PoolTicks__factory.abi, - bytecode: EphemeralPCSV3PoolTicks__factory.bytecode, - args: [pool, tickLower, tickUpper], - } - : { - abi: EphemeralPoolTicks__factory.abi, - bytecode: EphemeralPoolTicks__factory.bytecode, - args: [pool, tickLower, tickUpper], - }, + { + abi: EphemeralPoolTicks__factory.abi, + bytecode: EphemeralPoolTicks__factory.bytecode, + args: [ammToSolidityDexEnum(amm), pool, tickLower, tickUpper], + }, publicClient, blockNumber, ); @@ -103,18 +87,15 @@ export async function getTickBitmapSlots( publicClient: PublicClient, blockNumber?: bigint, ) { + if (amm === AutomatedMarketMakerEnum.enum.SLIPSTREAM) { + throw new Error("Not yet implemented for SLIPSTREAM"); + } return await callEphemeralContract( - amm === AutomatedMarketMakerEnum.enum.PANCAKESWAP_V3 - ? { - abi: EphemeralPCSV3PoolTickBitmap__factory.abi, - bytecode: EphemeralPCSV3PoolTickBitmap__factory.bytecode, - args: [pool], - } - : { - abi: EphemeralPoolTickBitmap__factory.abi, - bytecode: EphemeralPoolTickBitmap__factory.bytecode, - args: [pool], - }, + { + abi: EphemeralPoolTickBitmap__factory.abi, + bytecode: EphemeralPoolTickBitmap__factory.bytecode, + args: [ammToSolidityDexEnum(amm), pool], + }, publicClient, blockNumber, ); @@ -123,7 +104,7 @@ export async function getTickBitmapSlots( export type PositionKey = AbiParametersToPrimitiveTypes< ExtractAbiFunction["inputs"], "inputs" ->[1][0]; +>[2][0]; export async function getPositionsSlots( amm: AutomatedMarketMakerEnum, @@ -132,18 +113,15 @@ export async function getPositionsSlots( publicClient: PublicClient, blockNumber?: bigint, ) { + if (amm === AutomatedMarketMakerEnum.enum.SLIPSTREAM) { + throw new Error("Not yet implemented for SLIPSTREAM"); + } return await callEphemeralContract( - amm === AutomatedMarketMakerEnum.enum.PANCAKESWAP_V3 - ? { - abi: EphemeralPCSV3PoolPositions__factory.abi, - bytecode: EphemeralPCSV3PoolPositions__factory.bytecode, - args: [pool, keys], - } - : { - abi: EphemeralPoolPositions__factory.abi, - bytecode: EphemeralPoolPositions__factory.bytecode, - args: [pool, keys], - }, + { + abi: EphemeralPoolPositions__factory.abi, + bytecode: EphemeralPoolPositions__factory.bytecode, + args: [ammToSolidityDexEnum(amm), pool, keys], + }, publicClient, blockNumber, ); diff --git a/src/viem/positionLens.ts b/src/viem/positionLens.ts index f77771c..3fcfa03 100644 --- a/src/viem/positionLens.ts +++ b/src/viem/positionLens.ts @@ -5,20 +5,7 @@ import { EphemeralGetPosition__factory, EphemeralGetPositions__factory, } from "../../typechain"; -import { AutomatedMarketMakerEnum } from "./poolLens"; - -function ammToSolidityDexEnum(amm: AutomatedMarketMakerEnum): number { - if (amm === AutomatedMarketMakerEnum.enum.UNISWAP_V3) { - return 0; - } - if (amm === AutomatedMarketMakerEnum.enum.PANCAKESWAP_V3) { - return 1; - } - if (amm === AutomatedMarketMakerEnum.enum.SLIPSTREAM) { - return 2; - } - throw new Error(`Unexpected AMM: ${amm}`); -} +import { ammToSolidityDexEnum, AutomatedMarketMakerEnum } from "./amm"; /** * Get the position details in a single call by deploying an ephemeral contract via `eth_call` diff --git a/test/foundry/Base.t.sol b/test/foundry/Base.t.sol index 1ca4a4d..f79712c 100644 --- a/test/foundry/Base.t.sol +++ b/test/foundry/Base.t.sol @@ -41,6 +41,7 @@ abstract contract BaseTest is address internal token0; address internal token1; uint24 internal constant fee = 500; + int24 internal constant tickSpacingSlipStream = 100; address internal factory; address internal pool; @@ -53,7 +54,11 @@ abstract contract BaseTest is factory = INPM(npm).factory(); WETH = INPM(npm).WETH9(); (token0, token1) = (WETH < USDC).switchIf(USDC, WETH); - pool = IUniswapV3Factory(factory).getPool(token0, token1, fee); + if (dex == DEX.SlipStream) { + pool = ISlipStreamCLFactory(factory).getPool(token0, token1, tickSpacingSlipStream); + } else { + pool = IUniswapV3Factory(factory).getPool(token0, token1, fee); + } tickSpacing = V3PoolCallee.wrap(pool).tickSpacing(); token0Unit = 10 ** IERC20Metadata(token0).decimals(); token1Unit = 10 ** IERC20Metadata(token1).decimals(); diff --git a/test/foundry/PoolLens.t.sol b/test/foundry/PoolLens.t.sol index d36c7f4..8cc9a9b 100644 --- a/test/foundry/PoolLens.t.sol +++ b/test/foundry/PoolLens.t.sol @@ -10,7 +10,7 @@ import "./Base.t.sol"; contract PoolLensTest is BaseTest, PoolUtils { function test_GetSlots() public { - try new EphemeralPoolSlots(V3PoolCallee.wrap(pool)) {} catch (bytes memory returnData) { + try new EphemeralPoolSlots(dex, V3PoolCallee.wrap(pool)) {} catch (bytes memory returnData) { Slot[] memory slots = abi.decode(returnData, (Slot[])); uint256 length = slots.length; for (uint256 i; i < length; ++i) { @@ -21,7 +21,7 @@ contract PoolLensTest is BaseTest, PoolUtils { function test_GetPopulatedTicksInRange() public { int24 tick = currentTick(); - try new EphemeralPoolTicks(V3PoolCallee.wrap(pool), tick, tick) {} catch (bytes memory returnData) { + try new EphemeralPoolTicks(dex, 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) { @@ -31,7 +31,7 @@ contract PoolLensTest is BaseTest, PoolUtils { } function test_GetTickBitmap() public { - try new EphemeralPoolTickBitmap(V3PoolCallee.wrap(pool)) {} catch (bytes memory returnData) { + try new EphemeralPoolTickBitmap(dex, V3PoolCallee.wrap(pool)) {} catch (bytes memory returnData) { Slot[] memory slots = abi.decode(returnData, (Slot[])); uint256 length = slots.length; for (uint256 i; i < length; ++i) { @@ -47,7 +47,7 @@ contract PoolLensTest is BaseTest, PoolUtils { /// forge-config: default.fuzz.runs = 16 /// forge-config: ci.fuzz.runs = 16 - function testFuzz_GetPositions(int24 tickLower, int24 tickUpper) public { + function testFuzz_GetPositions(int24 tickLower, int24 tickUpper) public virtual { (tickLower, tickUpper) = prepTicks(tickLower, tickUpper); uint256 amount0Desired = token0Unit; uint256 amount1Desired = token1Unit; @@ -60,7 +60,7 @@ contract PoolLensTest is BaseTest, PoolUtils { tickLower -= tickSpacing; tickUpper += tickSpacing; } - try new EphemeralPoolPositions(V3PoolCallee.wrap(pool), keys) {} catch (bytes memory returnData) { + try new EphemeralPoolPositions(dex, 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) { @@ -70,69 +70,16 @@ contract PoolLensTest is BaseTest, PoolUtils { } } -contract PCSV3PoolLensTest is BaseTest, PoolUtils { +contract PCSV3PoolLensTest is PoolLensTest { function setUp() public override { 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); - } - + // Trivially override so that the "forge-config" settings are applied. /// 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(uint128).max); - deal(token1, address(this), type(uint128).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))); - } - } + function testFuzz_GetPositions(int24 tickLower, int24 tickUpper) public override { + super.testFuzz_GetPositions(tickLower, tickUpper); } } diff --git a/test/foundry/PositionLens.t.sol b/test/foundry/PositionLens.t.sol index a489c42..01d5a27 100644 --- a/test/foundry/PositionLens.t.sol +++ b/test/foundry/PositionLens.t.sol @@ -9,17 +9,16 @@ import "contracts/EphemeralAllPositionsByOwner.sol"; import "contracts/EphemeralGetPosition.sol"; import "contracts/EphemeralGetPositions.sol"; import "contracts/PositionLens.sol"; +import "contracts/interfaces/ISlipStreamCLPool.sol"; import "./Base.t.sol"; contract PositionLensTest is BaseTest { PositionLens internal positionLens; int24 internal _tickLower; int24 internal _tickUpper; - uint256 internal lastTokenId; function setUp() public virtual override { super.setUp(); - lastTokenId = INPM(npm).totalSupply(); positionLens = new PositionLens(); int24 tick = currentTick(); _tickLower = tick - tickSpacing; @@ -44,6 +43,7 @@ contract PositionLensTest is BaseTest { function test_GetFeesOwed() public { (uint256 fees0, uint256 fees1) = positionLens.getFeesOwed( + dex, V3PoolCallee.wrap(pool), address(this), _tickLower, @@ -52,7 +52,7 @@ contract PositionLensTest is BaseTest { assertEq(fees0, 0); assertEq(fees1, 0); generateFees(); - (fees0, fees1) = positionLens.getFeesOwed(V3PoolCallee.wrap(pool), address(this), _tickLower, _tickUpper); + (fees0, fees1) = positionLens.getFeesOwed(dex, V3PoolCallee.wrap(pool), address(this), _tickLower, _tickUpper); IUniswapV3Pool(pool).burn(_tickLower, _tickUpper, 0); (uint128 amount0, uint128 amount1) = IUniswapV3Pool(pool).collect( address(this), @@ -68,6 +68,7 @@ contract PositionLensTest is BaseTest { function test_GetTotalAmounts() public { generateFees(); (uint256 amount0, uint256 amount1) = positionLens.getTotalAmounts( + dex, V3PoolCallee.wrap(pool), address(this), _tickLower, @@ -88,7 +89,13 @@ contract PositionLensTest is BaseTest { assertEq(collect1, amount1); } - function verifyPosition(PositionState memory pos) internal view { + function verifyTokenDecimals(address token, uint8 decimalsToCheck) internal view { + try IERC20Metadata(token).decimals() returns (uint8 decimals) { + assertEq(decimals, decimalsToCheck, "decimals"); + } catch {} + } + + function verifyPosition(PositionState memory pos) internal view virtual { { assertEq(pos.owner, INPM(npm).ownerOf(pos.tokenId), "owner"); (, , address token0, , uint24 fee, int24 tickLower, , uint128 liquidity, , , , ) = IUniV3NPM(npm).positions( @@ -101,11 +108,7 @@ contract PositionLensTest is BaseTest { assertEq(liquidity, pos.position.liquidity, "liquidity"); } { - address pool = IUniswapV3Factory(factory).getPool( - pos.position.token0, - pos.position.token1, - pos.poolFee - ); + address pool = IUniswapV3Factory(factory).getPool(pos.position.token0, pos.position.token1, pos.poolFee); ( uint160 sqrtPriceX96, int24 tick, @@ -124,14 +127,14 @@ contract PositionLensTest is BaseTest { assertEq(unlocked, pos.slot0.unlocked, "unlocked"); assertEq(IUniswapV3Pool(pool).liquidity(), pos.activeLiquidity, "liquidity"); } - assertEq(IERC20Metadata(pos.position.token0).decimals(), pos.decimals0, "decimals0"); - assertEq(IERC20Metadata(pos.position.token1).decimals(), pos.decimals1, "decimals1"); + verifyTokenDecimals(pos.position.token0, pos.decimals0); + verifyTokenDecimals(pos.position.token1, pos.decimals1); } /// forge-config: default.fuzz.runs = 16 /// forge-config: ci.fuzz.runs = 16 - function testFuzz_GetPosition(uint256 tokenId) public virtual { - tokenId = bound(tokenId, 1, 200); + function testFuzz_GetPosition(uint256 tokenIndex) public virtual { + uint256 tokenId = INPM(npm).tokenByIndex(bound(tokenIndex, 0, 200)); try new EphemeralGetPosition(dex, npm, tokenId) {} catch (bytes memory returnData) { PositionState memory pos = abi.decode(returnData, (PositionState)); verifyPosition(pos); @@ -139,10 +142,10 @@ contract PositionLensTest is BaseTest { } function test_GetPositions() public virtual { - uint256 startTokenId = 100; + uint256 startTokenIndex = 100; uint256[] memory tokenIds = new uint256[](10); for (uint256 i; i < 10; ++i) { - tokenIds[i] = startTokenId + i; + tokenIds[i] = INPM(npm).tokenByIndex(startTokenIndex + i); } try new EphemeralGetPositions(dex, npm, tokenIds) {} catch (bytes memory returnData) { PositionState[] memory positions = abi.decode(returnData, (PositionState[])); @@ -155,7 +158,7 @@ contract PositionLensTest is BaseTest { } function test_AllPositions() public { - address owner = INPM(npm).ownerOf(lastTokenId); + address owner = INPM(npm).ownerOf(INPM(npm).tokenByIndex(0)); try new EphemeralAllPositionsByOwner(dex, npm, owner) {} catch (bytes memory returnData) { PositionState[] memory positions = abi.decode(returnData, (PositionState[])); uint256 length = positions.length; @@ -176,7 +179,60 @@ contract PCSV3PositionLensTest is PositionLensTest { // 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); + function testFuzz_GetPosition(uint256 tokenIndex) public override { + super.testFuzz_GetPosition(tokenIndex); + } +} + +contract SlipStreamPositionLensTest is PositionLensTest { + function setUp() public override { + dex = DEX.SlipStream; + 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 tokenIndex) public override { + super.testFuzz_GetPosition(tokenIndex); + } + + function verifyPosition(PositionState memory pos) internal view override { + { + assertEq(pos.owner, INPM(npm).ownerOf(pos.tokenId), "owner"); + (, , address token0, , int24 tickSpacing, int24 tickLower, , uint128 liquidity, , , , ) = ISlipStreamNPM( + npm + ).positions(pos.tokenId); + assertEq(token0, pos.position.token0, "token0"); + assertEq(uint24(tickSpacing), pos.position.feeOrTickSpacing, "feeOrTickSpacing"); + assertEq(tickSpacing, pos.poolTickSpacing, "poolTickSpacing"); + assertEq(tickLower, pos.position.tickLower, "tickLower"); + assertEq(liquidity, pos.position.liquidity, "liquidity"); + } + { + address pool = ISlipStreamCLFactory(ISlipStreamNPM(npm).factory()).getPool( + pos.position.token0, + pos.position.token1, + pos.poolTickSpacing + ); + ( + uint160 sqrtPriceX96, + int24 tick, + uint16 observationIndex, + uint16 observationCardinality, + uint16 observationCardinalityNext, + bool unlocked + ) = ISlipStreamCLPool(pool).slot0(); + assertEq(sqrtPriceX96, pos.slot0.sqrtPriceX96, "sqrtPriceX96"); + assertEq(tick, pos.slot0.tick, "tick"); + assertEq(observationIndex, pos.slot0.observationIndex, "observationIndex"); + assertEq(observationCardinality, pos.slot0.observationCardinality, "observationCardinality"); + assertEq(observationCardinalityNext, pos.slot0.observationCardinalityNext, "observationCardinalityNext"); + assertEq(0, pos.slot0.feeProtocol, "feeProtocol"); + assertEq(unlocked, pos.slot0.unlocked, "unlocked"); + assertEq(IUniswapV3Pool(pool).liquidity(), pos.activeLiquidity, "liquidity"); + } + verifyTokenDecimals(pos.position.token0, pos.decimals0); + verifyTokenDecimals(pos.position.token1, pos.decimals1); } } diff --git a/test/foundry/StorageLens.t.sol b/test/foundry/StorageLens.t.sol index 9c5d155..be507d6 100644 --- a/test/foundry/StorageLens.t.sol +++ b/test/foundry/StorageLens.t.sol @@ -25,7 +25,7 @@ contract StorageLensTest is BaseTest { /// forge-config: default.fuzz.runs = 16 /// forge-config: ci.fuzz.runs = 16 - function testFuzz_extsload(bytes32 slot) public { + function testFuzz_extsload(bytes32 slot) public virtual { bytes32[] memory slots = new bytes32[](1); slots[0] = slot; bytes32[] memory values = EphemeralStorageLens(pool).extsload(slots); @@ -38,4 +38,25 @@ contract PCSV3StorageLensTest is StorageLensTest { 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_extsload(bytes32 slot) public override { + super.testFuzz_extsload(slot); + } +} + +contract SlipStreamStorageLensTest is StorageLensTest { + function setUp() public override { + dex = DEX.SlipStream; + 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_extsload(bytes32 slot) public override { + super.testFuzz_extsload(slot); + } } diff --git a/test/foundry/TickLens.t.sol b/test/foundry/TickLens.t.sol index cfd6ae9..38047f6 100644 --- a/test/foundry/TickLens.t.sol +++ b/test/foundry/TickLens.t.sol @@ -10,16 +10,28 @@ contract TickLensTest is BaseTest, PoolUtils { function verifyTicks(PopulatedTick[] memory populatedTicks) internal view { for (uint256 i; i < populatedTicks.length; ++i) { PopulatedTick memory populatedTick = populatedTicks[i]; - ( - uint128 liquidityGross, - int128 liquidityNet, - uint256 feeGrowthOutside0X128, - uint256 feeGrowthOutside1X128, - , - , - , + uint128 liquidityGross; + int128 liquidityNet; + uint256 feeGrowthOutside0X128; + uint256 feeGrowthOutside1X128; + if (dex == DEX.SlipStream) { + ( + liquidityGross, + liquidityNet, + , + feeGrowthOutside0X128, + feeGrowthOutside1X128, + , + , + , + , - ) = IUniswapV3Pool(pool).ticks(populatedTick.tick); + ) = ISlipStreamCLPool(pool).ticks(populatedTick.tick); + } else { + (liquidityGross, liquidityNet, feeGrowthOutside0X128, feeGrowthOutside1X128, , , , ) = IUniswapV3Pool( + pool + ).ticks(populatedTick.tick); + } assertEq(liquidityGross, populatedTick.liquidityGross, "liquidityGross"); assertEq(liquidityNet, populatedTick.liquidityNet, "liquidityNet"); assertEq(feeGrowthOutside0X128, populatedTick.feeGrowthOutside0X128, "feeGrowthOutside0X128"); @@ -31,6 +43,7 @@ contract TickLensTest is BaseTest, PoolUtils { int24 tick = currentTick(); try new EphemeralGetPopulatedTicksInRange( + dex, V3PoolCallee.wrap(pool), tick - 128 * tickSpacing, tick + 128 * tickSpacing @@ -49,3 +62,10 @@ contract PCSV3TickLensTest is TickLensTest { super.setUp(); } } + +contract SlipStreamTickLensTest is TickLensTest { + function setUp() public override { + dex = DEX.SlipStream; + super.setUp(); + } +} diff --git a/test/hardhat/SlipStreamPool_abi.json b/test/hardhat/SlipStreamPool_abi.json deleted file mode 100644 index 731bdaf..0000000 --- a/test/hardhat/SlipStreamPool_abi.json +++ /dev/null @@ -1,1309 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "indexed": true, - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - }, - { - "indexed": false, - "internalType": "uint128", - "name": "amount", - "type": "uint128" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount0", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount1", - "type": "uint256" - } - ], - "name": "Burn", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": true, - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "indexed": true, - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - }, - { - "indexed": false, - "internalType": "uint128", - "name": "amount0", - "type": "uint128" - }, - { - "indexed": false, - "internalType": "uint128", - "name": "amount1", - "type": "uint128" - } - ], - "name": "Collect", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint128", - "name": "amount0", - "type": "uint128" - }, - { - "indexed": false, - "internalType": "uint128", - "name": "amount1", - "type": "uint128" - } - ], - "name": "CollectFees", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount0", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount1", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "paid0", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "paid1", - "type": "uint256" - } - ], - "name": "Flash", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint16", - "name": "observationCardinalityNextOld", - "type": "uint16" - }, - { - "indexed": false, - "internalType": "uint16", - "name": "observationCardinalityNextNew", - "type": "uint16" - } - ], - "name": "IncreaseObservationCardinalityNext", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint160", - "name": "sqrtPriceX96", - "type": "uint160" - }, - { - "indexed": false, - "internalType": "int24", - "name": "tick", - "type": "int24" - } - ], - "name": "Initialize", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "indexed": true, - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - }, - { - "indexed": false, - "internalType": "uint128", - "name": "amount", - "type": "uint128" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount0", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount1", - "type": "uint256" - } - ], - "name": "Mint", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "feeProtocol0Old", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "feeProtocol1Old", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "feeProtocol0New", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "feeProtocol1New", - "type": "uint8" - } - ], - "name": "SetFeeProtocol", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": false, - "internalType": "int256", - "name": "amount0", - "type": "int256" - }, - { - "indexed": false, - "internalType": "int256", - "name": "amount1", - "type": "int256" - }, - { - "indexed": false, - "internalType": "uint160", - "name": "sqrtPriceX96", - "type": "uint160" - }, - { - "indexed": false, - "internalType": "uint128", - "name": "liquidity", - "type": "uint128" - }, - { - "indexed": false, - "internalType": "int24", - "name": "tick", - "type": "int24" - } - ], - "name": "Swap", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - }, - { - "internalType": "uint128", - "name": "amount", - "type": "uint128" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "burn", - "outputs": [ - { - "internalType": "uint256", - "name": "amount0", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount1", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - }, - { - "internalType": "uint128", - "name": "amount", - "type": "uint128" - } - ], - "name": "burn", - "outputs": [ - { - "internalType": "uint256", - "name": "amount0", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount1", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - }, - { - "internalType": "uint128", - "name": "amount0Requested", - "type": "uint128" - }, - { - "internalType": "uint128", - "name": "amount1Requested", - "type": "uint128" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "collect", - "outputs": [ - { - "internalType": "uint128", - "name": "amount0", - "type": "uint128" - }, - { - "internalType": "uint128", - "name": "amount1", - "type": "uint128" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - }, - { - "internalType": "uint128", - "name": "amount0Requested", - "type": "uint128" - }, - { - "internalType": "uint128", - "name": "amount1Requested", - "type": "uint128" - } - ], - "name": "collect", - "outputs": [ - { - "internalType": "uint128", - "name": "amount0", - "type": "uint128" - }, - { - "internalType": "uint128", - "name": "amount1", - "type": "uint128" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "collectFees", - "outputs": [ - { - "internalType": "uint128", - "name": "amount0", - "type": "uint128" - }, - { - "internalType": "uint128", - "name": "amount1", - "type": "uint128" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "factory", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "factoryRegistry", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "fee", - "outputs": [ - { - "internalType": "uint24", - "name": "", - "type": "uint24" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "feeGrowthGlobal0X128", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "feeGrowthGlobal1X128", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount0", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount1", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "flash", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "gauge", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "gaugeFees", - "outputs": [ - { - "internalType": "uint128", - "name": "token0", - "type": "uint128" - }, - { - "internalType": "uint128", - "name": "token1", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - }, - { - "internalType": "uint256", - "name": "_rewardGrowthGlobalX128", - "type": "uint256" - } - ], - "name": "getRewardGrowthInside", - "outputs": [ - { - "internalType": "uint256", - "name": "rewardGrowthInside", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "observationCardinalityNext", - "type": "uint16" - } - ], - "name": "increaseObservationCardinalityNext", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_factory", - "type": "address" - }, - { - "internalType": "address", - "name": "_token0", - "type": "address" - }, - { - "internalType": "address", - "name": "_token1", - "type": "address" - }, - { - "internalType": "int24", - "name": "_tickSpacing", - "type": "int24" - }, - { - "internalType": "address", - "name": "_factoryRegistry", - "type": "address" - }, - { - "internalType": "uint160", - "name": "_sqrtPriceX96", - "type": "uint160" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "lastUpdated", - "outputs": [ - { - "internalType": "uint32", - "name": "", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "liquidity", - "outputs": [ - { - "internalType": "uint128", - "name": "", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "maxLiquidityPerTick", - "outputs": [ - { - "internalType": "uint128", - "name": "", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - }, - { - "internalType": "uint128", - "name": "amount", - "type": "uint128" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "mint", - "outputs": [ - { - "internalType": "uint256", - "name": "amount0", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount1", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "nft", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "observations", - "outputs": [ - { - "internalType": "uint32", - "name": "blockTimestamp", - "type": "uint32" - }, - { - "internalType": "int56", - "name": "tickCumulative", - "type": "int56" - }, - { - "internalType": "uint160", - "name": "secondsPerLiquidityCumulativeX128", - "type": "uint160" - }, - { - "internalType": "bool", - "name": "initialized", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32[]", - "name": "secondsAgos", - "type": "uint32[]" - } - ], - "name": "observe", - "outputs": [ - { - "internalType": "int56[]", - "name": "tickCumulatives", - "type": "int56[]" - }, - { - "internalType": "uint160[]", - "name": "secondsPerLiquidityCumulativeX128s", - "type": "uint160[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "periodFinish", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "positions", - "outputs": [ - { - "internalType": "uint128", - "name": "liquidity", - "type": "uint128" - }, - { - "internalType": "uint256", - "name": "feeGrowthInside0LastX128", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "feeGrowthInside1LastX128", - "type": "uint256" - }, - { - "internalType": "uint128", - "name": "tokensOwed0", - "type": "uint128" - }, - { - "internalType": "uint128", - "name": "tokensOwed1", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "rewardGrowthGlobalX128", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "rewardRate", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "rewardReserve", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "rollover", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_gauge", - "type": "address" - }, - { - "internalType": "address", - "name": "_nft", - "type": "address" - } - ], - "name": "setGaugeAndPositionManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "slot0", - "outputs": [ - { - "internalType": "uint160", - "name": "sqrtPriceX96", - "type": "uint160" - }, - { - "internalType": "int24", - "name": "tick", - "type": "int24" - }, - { - "internalType": "uint16", - "name": "observationIndex", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "observationCardinality", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "observationCardinalityNext", - "type": "uint16" - }, - { - "internalType": "bool", - "name": "unlocked", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - } - ], - "name": "snapshotCumulativesInside", - "outputs": [ - { - "internalType": "int56", - "name": "tickCumulativeInside", - "type": "int56" - }, - { - "internalType": "uint160", - "name": "secondsPerLiquidityInsideX128", - "type": "uint160" - }, - { - "internalType": "uint32", - "name": "secondsInside", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "int128", - "name": "stakedLiquidityDelta", - "type": "int128" - }, - { - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - }, - { - "internalType": "bool", - "name": "positionUpdate", - "type": "bool" - } - ], - "name": "stake", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "stakedLiquidity", - "outputs": [ - { - "internalType": "uint128", - "name": "", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "bool", - "name": "zeroForOne", - "type": "bool" - }, - { - "internalType": "int256", - "name": "amountSpecified", - "type": "int256" - }, - { - "internalType": "uint160", - "name": "sqrtPriceLimitX96", - "type": "uint160" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "swap", - "outputs": [ - { - "internalType": "int256", - "name": "amount0", - "type": "int256" - }, - { - "internalType": "int256", - "name": "amount1", - "type": "int256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_rewardRate", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_rewardReserve", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_periodFinish", - "type": "uint256" - } - ], - "name": "syncReward", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "int16", - "name": "", - "type": "int16" - } - ], - "name": "tickBitmap", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "tickSpacing", - "outputs": [ - { - "internalType": "int24", - "name": "", - "type": "int24" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "int24", - "name": "", - "type": "int24" - } - ], - "name": "ticks", - "outputs": [ - { - "internalType": "uint128", - "name": "liquidityGross", - "type": "uint128" - }, - { - "internalType": "int128", - "name": "liquidityNet", - "type": "int128" - }, - { - "internalType": "int128", - "name": "stakedLiquidityNet", - "type": "int128" - }, - { - "internalType": "uint256", - "name": "feeGrowthOutside0X128", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "feeGrowthOutside1X128", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rewardGrowthOutsideX128", - "type": "uint256" - }, - { - "internalType": "int56", - "name": "tickCumulativeOutside", - "type": "int56" - }, - { - "internalType": "uint160", - "name": "secondsPerLiquidityOutsideX128", - "type": "uint160" - }, - { - "internalType": "uint32", - "name": "secondsOutside", - "type": "uint32" - }, - { - "internalType": "bool", - "name": "initialized", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "token0", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "token1", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "unstakedFee", - "outputs": [ - { - "internalType": "uint24", - "name": "", - "type": "uint24" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "updateRewardsGrowthGlobal", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] \ No newline at end of file diff --git a/test/hardhat/pcsv3_test.ts b/test/hardhat/pcsv3_test.ts index 4cf586e..68f3e0c 100644 --- a/test/hardhat/pcsv3_test.ts +++ b/test/hardhat/pcsv3_test.ts @@ -3,7 +3,6 @@ import { expect } from "chai"; import { ContractFunctionReturnType, createPublicClient, getContract, http, toHex } from "viem"; import { bsc } from "viem/chains"; import { - AutomatedMarketMakerEnum, getAllPositionsByOwner, getPopulatedTicksInRange, getPositionDetails, @@ -22,6 +21,7 @@ import { } from "../../typechain"; import { computePoolAddress } from "@pancakeswap/v3-sdk"; import { Token } from "@pancakeswap/sdk"; +import { AutomatedMarketMakerEnum } from "../../src/viem/amm"; const AMM = AutomatedMarketMakerEnum.enum.PANCAKESWAP_V3; const PCSV3_NPM = "0x46A15B0b27311cedF172AB29E4f4766fbE7F4364"; @@ -77,7 +77,7 @@ describe("Pool lens test with PCSV3 on BSC", () => { it("Test getting populated ticks", async () => { const [, tickCurrent] = await poolContract.read.slot0({ blockNumber }); - const ticks = await getPopulatedTicksInRange(pool, tickCurrent, tickCurrent, publicClient, blockNumber); + const ticks = await getPopulatedTicksInRange(AMM, pool, tickCurrent, tickCurrent, publicClient, blockNumber); await Promise.all( ticks.map(async ({ tick, liquidityGross, liquidityNet }) => { const [_liquidityGross, _liquidityNet] = await poolContract.read.ticks([tick], { blockNumber }); diff --git a/test/hardhat/slipstream_test.ts b/test/hardhat/slipstream_test.ts index 648f6ba..b9aa3cf 100644 --- a/test/hardhat/slipstream_test.ts +++ b/test/hardhat/slipstream_test.ts @@ -1,27 +1,21 @@ -import { TickMath } from "@uniswap/v3-sdk"; import { expect } from "chai"; import { Address, ContractFunctionReturnType, createPublicClient, getContract, http, toHex } from "viem"; import { - AutomatedMarketMakerEnum, getAllPositionsByOwner, getPopulatedTicksInRange, getPositionDetails, getPositions, - getPositionsSlots, - getStaticSlots, getStorageAt, - getTickBitmapSlots, - getTicksSlots, } from "../../src/viem"; import { EphemeralGetPositions__factory, - EphemeralPoolSlots__factory, ISlipStreamCLFactory__factory, ISlipStreamNonfungiblePositionManager__factory, } from "../../typechain"; import { base } from "viem/chains"; import 'dotenv/config'; -import SlipStreamPoolAbi from './SlipStreamPool_abi.json'; +import { AutomatedMarketMakerEnum } from "../../src/viem/amm"; +import { ISlipStreamCLPool__factory } from "../../typechain/factories/contracts/interfaces"; const AMM = AutomatedMarketMakerEnum.enum.SLIPSTREAM; const SLIPSTREAM_NPM = "0x827922686190790b37229fd06084350E74485b72"; @@ -56,7 +50,7 @@ describe("Pool lens test with SlipStream on Base", () => { console.log(`Running SlipStream tests on Base mainnet at block number ${blockNumber}...`); pool = await factoryContract.read.getPool([WETH_ADDRESS, USDC_ADDRESS, /*tickSpacing=*/100], { blockNumber }); poolContract = getContract({ - abi: SlipStreamPoolAbi, + abi: ISlipStreamCLPool__factory.abi, client: publicClient, address: pool, }); @@ -79,7 +73,7 @@ describe("Pool lens test with SlipStream on Base", () => { it("Test getting populated ticks", async () => { const [, tickCurrent] = await poolContract.read.slot0({ blockNumber }); - const ticks = await getPopulatedTicksInRange(pool, tickCurrent, tickCurrent, publicClient, blockNumber); + const ticks = await getPopulatedTicksInRange(AMM, pool, tickCurrent, tickCurrent, publicClient, blockNumber); await Promise.all( ticks.map(async ({ tick, liquidityGross, liquidityNet }) => { const [_liquidityGross, _liquidityNet] = await poolContract.read.ticks([tick], { blockNumber }); @@ -98,7 +92,7 @@ describe("Pool lens test with SlipStream on Base", () => { expect(tokenId).to.be.eq(43484n); const [_sqrtPriceX96, _tick] = await getContract({ address: await factoryContract.read.getPool([token0, token1, tickSpacing], { blockNumber }), - abi: SlipStreamPoolAbi, + abi: ISlipStreamCLPool__factory.abi, client: publicClient, }).read.slot0({ blockNumber }); expect(sqrtPriceX96).to.be.eq(_sqrtPriceX96); @@ -140,47 +134,4 @@ describe("Pool lens test with SlipStream on Base", () => { const posArr = await getAllPositionsByOwner(AMM, SLIPSTREAM_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 - 1000n, - 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); - }); }); diff --git a/test/hardhat/univ3_test.ts b/test/hardhat/univ3_test.ts index d66b358..42f9ffa 100644 --- a/test/hardhat/univ3_test.ts +++ b/test/hardhat/univ3_test.ts @@ -2,7 +2,6 @@ import { TickMath, computePoolAddress } from "@uniswap/v3-sdk"; import { expect } from "chai"; import { Address, ContractFunctionReturnType, createPublicClient, getContract, http, toHex } from "viem"; import { - AutomatedMarketMakerEnum, getAllPositionsByOwner, getPopulatedTicksInRange, getPositionDetails, @@ -21,6 +20,7 @@ import { } from "../../typechain"; import { mainnet } from "viem/chains"; import { Token } from "@uniswap/sdk-core"; +import { AutomatedMarketMakerEnum } from "../../src/viem/amm"; const AMM = AutomatedMarketMakerEnum.enum.UNISWAP_V3; const UNIV3_NPM = "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"; @@ -76,7 +76,7 @@ describe("Pool lens test with UniV3 on mainnet", () => { it("Test getting populated ticks", async () => { const [, tickCurrent] = await poolContract.read.slot0({ blockNumber }); - const ticks = await getPopulatedTicksInRange(pool, tickCurrent, tickCurrent, publicClient, blockNumber); + const ticks = await getPopulatedTicksInRange(AMM, pool, tickCurrent, tickCurrent, publicClient, blockNumber); await Promise.all( ticks.map(async ({ tick, liquidityGross, liquidityNet }) => { const [_liquidityGross, _liquidityNet] = await poolContract.read.ticks([tick], { blockNumber }); diff --git a/yarn.lock b/yarn.lock index c7d9991..3eb9f87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -429,65 +429,53 @@ resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== -"@nomicfoundation/edr-darwin-arm64@0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.3.2.tgz#be1f696d429aecdf47d292a75958551e7cc34294" - integrity sha512-l6wfSBUUbGJiCENT6272CDI8yoMuf0sZH56H5qz3HnAyVzenkOvmzyF6/lar54m986kdAQqWls4cLvDxiOuLxg== - -"@nomicfoundation/edr-darwin-x64@0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.3.2.tgz#efd2a5a302dc0a806fb2439a7e2c8ed7bd482ff8" - integrity sha512-OboExL7vEw+TRJQl3KkaEKU4K7PTdZPTInZ0fxMAtOpcWp7EKR+dQo68vc/iAOusB3xszHKxt7t+WpisItfdcg== - -"@nomicfoundation/edr-linux-arm64-gnu@0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.3.2.tgz#4d7d4f28e13d426b5e3cf9731dbfed573f819245" - integrity sha512-xtEK+1eg++3pHi6405NDXd80S3CGOFEGQIyVGCwjMGQFOLSzBGGCsrb/0GB4J19zd1o/8ftCd/HjZcbVAWWTLQ== - -"@nomicfoundation/edr-linux-arm64-musl@0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.3.2.tgz#10702ae4db386cc21efef9e6276bb56a09581e0a" - integrity sha512-3cIsskJOXQ1yEVsImmCacY7O03tUTiWrmd54F05PnPFrDLkjbzodQ3b2gUWzfbzUZWl67ZTJd1CvVSzpe7XGzw== - -"@nomicfoundation/edr-linux-x64-gnu@0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.3.2.tgz#a29590bea8d490aa2129f2cb7fab1559ec9d25ee" - integrity sha512-ouPdphHNsyO7wqwa4hwahC5WqBglK/fIvMmhR/SXNZ9qruIpsA8ZZKIURaHMOv/2h2BbNGcyTX9uEk6+5rK/MQ== - -"@nomicfoundation/edr-linux-x64-musl@0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.3.2.tgz#fe2b4c282c80c792b3a03e04d517d140a927cf92" - integrity sha512-sRhwhiPbkpJMOUwXW1FZw9ks6xWyQhIhM0E8o3TXEXKSPKTE6whQLEk1R37iFITaI36vb6rSwLKTU1cb32gCoA== - -"@nomicfoundation/edr-win32-arm64-msvc@0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-arm64-msvc/-/edr-win32-arm64-msvc-0.3.2.tgz#6f09cf20f7e7be3ea70f61888cde238d924ec369" - integrity sha512-IEwVealKfumu1HSSnama26yPuQC/uthRPK5IWtFsQUOGwOXaS1r9Bu7cGYH2jBHl3IT/JbxD4xzPq/2pM9uK0A== - -"@nomicfoundation/edr-win32-ia32-msvc@0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-ia32-msvc/-/edr-win32-ia32-msvc-0.3.2.tgz#ba4cd9744d95f491329efdfbefb527a8c9f5f31d" - integrity sha512-jYMnf6SFgguqROswwdsjJ1wvneD/5c16pVu9OD4DxNqhKNP5bHEw6L2N4DcJ89tpXMpJ6AlOpc0QuwzddiZ3tA== - -"@nomicfoundation/edr-win32-x64-msvc@0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.3.2.tgz#331e3cea1618134e8d9f4ccb999086af6362a3af" - integrity sha512-Byn4QuWczRy/DUUQM3WjglgX/jGVUURVFaUsmIhnGg//MPlCLawubBGRqsrMuvaYedlIIJ4I2rgKvZlxdgHrqg== - -"@nomicfoundation/edr@^0.3.1": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.3.2.tgz#a81500746f8f8c342606a9be04a9f6176f8e2d54" - integrity sha512-HGWtjibAK1mo4I2A7nJ/fXqe/J9G54OrSPJnnkY2K8TiXotYLShGd9GvHkae3PuFjTJKm6ZgBy7tveJj5yrCfw== - optionalDependencies: - "@nomicfoundation/edr-darwin-arm64" "0.3.2" - "@nomicfoundation/edr-darwin-x64" "0.3.2" - "@nomicfoundation/edr-linux-arm64-gnu" "0.3.2" - "@nomicfoundation/edr-linux-arm64-musl" "0.3.2" - "@nomicfoundation/edr-linux-x64-gnu" "0.3.2" - "@nomicfoundation/edr-linux-x64-musl" "0.3.2" - "@nomicfoundation/edr-win32-arm64-msvc" "0.3.2" - "@nomicfoundation/edr-win32-ia32-msvc" "0.3.2" - "@nomicfoundation/edr-win32-x64-msvc" "0.3.2" +"@nomicfoundation/edr-darwin-arm64@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.4.2.tgz#2ff98535f272c9f2a7d06eeda93fe7b207a348a4" + integrity sha512-S+hhepupfqpBvMa9M1PVS08sVjGXsLnjyAsjhrrsjsNuTHVLhKzhkguvBD5g4If5skrwgOaVqpag4wnQbd15kQ== + +"@nomicfoundation/edr-darwin-x64@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.4.2.tgz#001dcd0e7fa4c52046d283b0dc61e63a60c614dd" + integrity sha512-/zM94AUrXz6CmcsecRNHJ50jABDUFafmGc4iBmkfX/mTp4tVZj7XTyIogrQIt0FnTaeb4CgZoLap2+8tW/Uldg== + +"@nomicfoundation/edr-linux-arm64-gnu@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.4.2.tgz#6d19f8265c8ffb22e29bc5bbbb5d1913fe4b306b" + integrity sha512-TV3Pr2tFvvmCfPCi9PaCGLtqn+oLaPKfL2NWpnoCeFFdzDQXi2L930yP1oUPY5RXd78NLdVHMkEkbhb2b6Wuvg== + +"@nomicfoundation/edr-linux-arm64-musl@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.4.2.tgz#0b01aa405fdc8048c7a8e95c737f29b437536a30" + integrity sha512-PALwrLBk1M9rolXyhSX8xdhe5jL0qf/PgiCIF7W7lUyVKrI/I0oiU0EHDk/Xw7yi2UJg4WRyhhZoHYa0g4g8Qg== + +"@nomicfoundation/edr-linux-x64-gnu@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.4.2.tgz#10959fd4db9b333d3e0559cb893e109611889af0" + integrity sha512-5svkftypDjAZ1LxV1onojlaqPRxrTEjJLkrUwLL+Fao5ZMe7aTnk5QQ1Jv76gW6WYZnMXNgjPhRcnw3oSNrqFA== + +"@nomicfoundation/edr-linux-x64-musl@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.4.2.tgz#8de64a2dfd869dad930dd0eb9572a0593d382379" + integrity sha512-qiMlXQTggdH9zfOB4Eil4rQ95z8s7QdLJcOfz5Aym12qJNkCyF9hi4cc4dDCWA0CdI3x3oLbuf8qb81SF8R45w== + +"@nomicfoundation/edr-win32-x64-msvc@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.4.2.tgz#13ad4bab9fd68853930e1a3d87c78d69d1d0e2ef" + integrity sha512-hDkAb0iaMmGYwBY/rA1oCX8VpsezfQcHPEPIEGXEcWC3WbnOgIZo0Qkpu/g0OMtFOJSQlWLXvKZuV7blhnrQag== + +"@nomicfoundation/edr@^0.4.1": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.4.2.tgz#9d7550182d4f75d7510e265ebd3474c4f6fcb62a" + integrity sha512-U7v0HuZHfrsl/5FpUzuB2FYA0+FUglHHwiO6NhvLtNYKMZcPzdS6iUriMp/7GWs0SVxW3bAht9GinZPxdhVwWg== + dependencies: + "@nomicfoundation/edr-darwin-arm64" "0.4.2" + "@nomicfoundation/edr-darwin-x64" "0.4.2" + "@nomicfoundation/edr-linux-arm64-gnu" "0.4.2" + "@nomicfoundation/edr-linux-arm64-musl" "0.4.2" + "@nomicfoundation/edr-linux-x64-gnu" "0.4.2" + "@nomicfoundation/edr-linux-x64-musl" "0.4.2" + "@nomicfoundation/edr-win32-x64-msvc" "0.4.2" "@nomicfoundation/ethereumjs-common@4.0.4": version "4.0.4" @@ -519,10 +507,10 @@ "@nomicfoundation/ethereumjs-rlp" "5.0.4" ethereum-cryptography "0.1.3" -"@nomicfoundation/hardhat-foundry@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-foundry/-/hardhat-foundry-1.1.1.tgz#db72b1f33f9cfaecc27e67f69ad436f8710162d6" - integrity sha512-cXGCBHAiXas9Pg9MhMOpBVQCkWRYoRFG7GJJAph+sdQsfd22iRs5U5Vs9XmpGEQd1yEvYISQZMeE68Nxj65iUQ== +"@nomicfoundation/hardhat-foundry@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-foundry/-/hardhat-foundry-1.1.2.tgz#4f5aaa1803b8f5d974dcbc361beb72d49c815562" + integrity sha512-f5Vhj3m2qvKGpr6NAINYwNgILDsai8dVCsFb1rAVLkJxOmD2pAtfCmOH5SBVr9yUI5B1z9rbTwPBJVrqnb+PXQ== dependencies: chalk "^2.4.2" @@ -1380,10 +1368,10 @@ command-line-usage@^6.1.0: table-layout "^1.0.2" typical "^5.2.0" -commander@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" - integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== +commander@^8.1.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== concat-map@0.0.1: version "0.0.1" @@ -1676,17 +1664,6 @@ fp-ts@^1.0.0: resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a" integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A== -fs-extra@^0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" - integrity sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^2.1.0" - klaw "^1.0.0" - path-is-absolute "^1.0.0" - rimraf "^2.2.8" - fs-extra@^7.0.0, fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -1768,19 +1745,7 @@ glob@8.1.0: minimatch "^5.0.1" once "^1.3.0" -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -1792,14 +1757,14 @@ hardhat-watcher@^2.1.1: dependencies: chokidar "^3.5.3" -hardhat@^2.22.2: - version "2.22.2" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.2.tgz#0cadd7ec93bf39bab09f81603e75bc5e92acea3d" - integrity sha512-0xZ7MdCZ5sJem4MrvpQWLR3R3zGDoHw5lsR+pBFimqwagimIOn3bWuZv69KA+veXClwI1s/zpqgwPwiFrd4Dxw== +hardhat@^2.22.6: + version "2.22.6" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.6.tgz#d73caece246cd8219a1815554dabc31d400fa035" + integrity sha512-abFEnd9QACwEtSvZZGSmzvw7N3zhQN1cDKz5SLHAupfG24qTHofCjqvD5kT5Wwsq5XOL0ON1Mq5rr4v0XX5ciw== dependencies: "@ethersproject/abi" "^5.1.2" "@metamask/eth-sig-util" "^4.0.0" - "@nomicfoundation/edr" "^0.3.1" + "@nomicfoundation/edr" "^0.4.1" "@nomicfoundation/ethereumjs-common" "4.0.4" "@nomicfoundation/ethereumjs-tx" "5.0.4" "@nomicfoundation/ethereumjs-util" "9.0.4" @@ -1833,7 +1798,7 @@ hardhat@^2.22.2: raw-body "^2.4.1" resolve "1.17.0" semver "^6.3.0" - solc "0.7.3" + solc "0.8.26" source-map-support "^0.5.13" stacktrace-parser "^0.1.10" tsort "0.0.1" @@ -2004,13 +1969,6 @@ jsbi@^3.1.4: resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.2.5.tgz#b37bb90e0e5c2814c1c2a1bcd8c729888a2e37d6" integrity sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ== -jsonfile@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - integrity sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw== - optionalDependencies: - graceful-fs "^4.1.6" - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -2036,13 +1994,6 @@ keccak@^3.0.0, keccak@^3.0.2: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -klaw@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" - integrity sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw== - optionalDependencies: - graceful-fs "^4.1.9" - locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -2131,7 +2082,7 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^3.0.4, minimatch@^3.1.1: +minimatch@^3.0.4: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -2400,11 +2351,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-from-string@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - resolve@1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" @@ -2412,13 +2358,6 @@ resolve@1.17.0: dependencies: path-parse "^1.0.6" -rimraf@^2.2.8: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -2505,18 +2444,16 @@ solady@^0.0.180: resolved "https://registry.yarnpkg.com/solady/-/solady-0.0.180.tgz#d806c84a0bf8b6e3d85a8fb0978980de086ff59e" integrity sha512-9QVCyMph+wk78Aq/GxtDAQg7dvNoVWx2dS2Zwf11XlwFKDZ+YJG2lrQsK9NEIth9NOebwjBXAYk4itdwOOE4aw== -solc@0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" - integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== +solc@0.8.26: + version "0.8.26" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.26.tgz#afc78078953f6ab3e727c338a2fefcd80dd5b01a" + integrity sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g== dependencies: command-exists "^1.2.8" - commander "3.0.2" + commander "^8.1.0" follow-redirects "^1.12.1" - fs-extra "^0.30.0" js-sha3 "0.8.0" memorystream "^0.3.1" - require-from-string "^2.0.0" semver "^5.5.0" tmp "0.0.33"