diff --git a/test/position-managers/Execute.t.sol b/test/position-managers/Execute.t.sol index 9c568936..1d2ccc77 100644 --- a/test/position-managers/Execute.t.sol +++ b/test/position-managers/Execute.t.sol @@ -1,182 +1,172 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.24; - -// import "forge-std/Test.sol"; -// import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; -// import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol"; -// import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; -// import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; -// import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol"; -// import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; -// import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; -// import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; -// import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; -// import {PoolSwapTest} from "@uniswap/v4-core/src/test/PoolSwapTest.sol"; -// import {LiquidityAmounts} from "../../contracts/libraries/LiquidityAmounts.sol"; -// import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; -// import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -// import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; -// import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol"; - -// import {IERC20} from "forge-std/interfaces/IERC20.sol"; -// import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; - -// import {INonfungiblePositionManager} from "../../contracts/interfaces/INonfungiblePositionManager.sol"; -// import {NonfungiblePositionManager} from "../../contracts/NonfungiblePositionManager.sol"; -// import {LiquidityRange, LiquidityRangeId, LiquidityRangeIdLibrary} from "../../contracts/types/LiquidityRange.sol"; - -// import {LiquidityFuzzers} from "../shared/fuzz/LiquidityFuzzers.sol"; - -// import {LiquidityOperations} from "../shared/LiquidityOperations.sol"; -// import {Planner} from "../utils/Planner.sol"; - -// contract ExecuteTest is Test, Deployers, GasSnapshot, LiquidityFuzzers, LiquidityOperations { -// using FixedPointMathLib for uint256; -// using CurrencyLibrary for Currency; -// using LiquidityRangeIdLibrary for LiquidityRange; -// using PoolIdLibrary for PoolKey; -// using SafeCast for uint256; -// using Planner for Planner.Plan; - -// PoolId poolId; -// address alice = makeAddr("ALICE"); -// address bob = makeAddr("BOB"); - -// uint256 constant STARTING_USER_BALANCE = 10_000_000 ether; - -// // expresses the fee as a wad (i.e. 3000 = 0.003e18 = 0.30%) -// uint256 FEE_WAD; - -// LiquidityRange range; - -// function setUp() public { -// Deployers.deployFreshManagerAndRouters(); -// Deployers.deployMintAndApprove2Currencies(); - -// (key, poolId) = initPool(currency0, currency1, IHooks(address(0)), 3000, SQRT_PRICE_1_1, ZERO_BYTES); -// FEE_WAD = uint256(key.fee).mulDivDown(FixedPointMathLib.WAD, 1_000_000); - -// lpm = new NonfungiblePositionManager(manager); -// IERC20(Currency.unwrap(currency0)).approve(address(lpm), type(uint256).max); -// IERC20(Currency.unwrap(currency1)).approve(address(lpm), type(uint256).max); - -// // Give tokens to Alice and Bob, with approvals -// IERC20(Currency.unwrap(currency0)).transfer(alice, STARTING_USER_BALANCE); -// IERC20(Currency.unwrap(currency1)).transfer(alice, STARTING_USER_BALANCE); -// IERC20(Currency.unwrap(currency0)).transfer(bob, STARTING_USER_BALANCE); -// IERC20(Currency.unwrap(currency1)).transfer(bob, STARTING_USER_BALANCE); -// vm.startPrank(alice); -// IERC20(Currency.unwrap(currency0)).approve(address(lpm), type(uint256).max); -// IERC20(Currency.unwrap(currency1)).approve(address(lpm), type(uint256).max); -// vm.stopPrank(); -// vm.startPrank(bob); -// IERC20(Currency.unwrap(currency0)).approve(address(lpm), type(uint256).max); -// IERC20(Currency.unwrap(currency1)).approve(address(lpm), type(uint256).max); -// vm.stopPrank(); - -// // define a reusable range -// range = LiquidityRange({poolKey: key, tickLower: -300, tickUpper: 300}); -// } - -// function test_execute_increaseLiquidity_once(uint256 initialLiquidity, uint256 liquidityToAdd) public { -// initialLiquidity = bound(initialLiquidity, 1e18, 1000e18); -// liquidityToAdd = bound(liquidityToAdd, 1e18, 1000e18); -// _mint(range, initialLiquidity, block.timestamp, address(this), ZERO_BYTES); -// uint256 tokenId = lpm.nextTokenId() - 1; - -// bytes[] memory data = new bytes[](1); -// data[0] = abi.encodeWithSelector( -// INonfungiblePositionManager.increaseLiquidity.selector, tokenId, liquidityToAdd, ZERO_BYTES, false -// ); - -// Currency[] memory currencies = new Currency[](2); -// currencies[0] = currency0; -// currencies[1] = currency1; -// lpm.modifyLiquidities(data, currencies); - -// (,, uint256 liquidity,,,,) = lpm.positions(address(this), range.toId()); -// assertEq(liquidity, initialLiquidity + liquidityToAdd); -// } - -// function test_execute_increaseLiquidity_twice( -// uint256 initialiLiquidity, -// uint256 liquidityToAdd, -// uint256 liquidityToAdd2 -// ) public { -// initialiLiquidity = bound(initialiLiquidity, 1e18, 1000e18); -// liquidityToAdd = bound(liquidityToAdd, 1e18, 1000e18); -// liquidityToAdd2 = bound(liquidityToAdd2, 1e18, 1000e18); -// _mint(range, initialiLiquidity, block.timestamp, address(this), ZERO_BYTES); -// uint256 tokenId = lpm.nextTokenId() - 1; - -// bytes[] memory data = new bytes[](2); -// data[0] = abi.encodeWithSelector( -// INonfungiblePositionManager.increaseLiquidity.selector, tokenId, liquidityToAdd, ZERO_BYTES, false -// ); -// data[1] = abi.encodeWithSelector( -// INonfungiblePositionManager.increaseLiquidity.selector, tokenId, liquidityToAdd2, ZERO_BYTES, false -// ); - -// Currency[] memory currencies = new Currency[](2); -// currencies[0] = currency0; -// currencies[1] = currency1; -// lpm.modifyLiquidities(data, currencies); - -// (,, uint256 liquidity,,,,) = lpm.positions(address(this), range.toId()); -// assertEq(liquidity, initialiLiquidity + liquidityToAdd + liquidityToAdd2); -// } - -// // this case doesnt make sense in real world usage, so it doesnt have a cool name. but its a good test case -// function test_execute_mintAndIncrease(uint256 intialLiquidity, uint256 liquidityToAdd) public { -// intialLiquidity = bound(intialLiquidity, 1e18, 1000e18); -// liquidityToAdd = bound(liquidityToAdd, 1e18, 1000e18); - -// uint256 tokenId = 1; // assume that the .mint() produces tokenId=1, to be used in increaseLiquidity -// bytes[] memory data = new bytes[](2); -// data[0] = abi.encodeWithSelector( -// INonfungiblePositionManager.mint.selector, -// range, -// intialLiquidity, -// block.timestamp + 1, -// address(this), -// ZERO_BYTES -// ); -// data[1] = abi.encodeWithSelector( -// INonfungiblePositionManager.increaseLiquidity.selector, tokenId, liquidityToAdd, ZERO_BYTES, false -// ); - -// Currency[] memory currencies = new Currency[](2); -// currencies[0] = currency0; -// currencies[1] = currency1; -// lpm.modifyLiquidities(data, currencies); - -// (,, uint256 liquidity,,,,) = lpm.positions(address(this), range.toId()); -// assertEq(liquidity, intialLiquidity + liquidityToAdd); -// } - -// // rebalance: burn and mint -// function test_execute_rebalance() public {} -// // coalesce: burn and increase -// function test_execute_coalesce() public {} -// // split: decrease and mint -// function test_execute_split() public {} -// // shift: decrease and increase -// function test_execute_shift() public {} -// // shard: collect and mint -// function test_execute_shard() public {} -// // feed: collect and increase -// function test_execute_feed() public {} - -// // transplant: burn and mint on different keys -// function test_execute_transplant() public {} -// // cross-coalesce: burn and increase on different keys -// function test_execute_crossCoalesce() public {} -// // cross-split: decrease and mint on different keys -// function test_execute_crossSplit() public {} -// // cross-shift: decrease and increase on different keys -// function test_execute_crossShift() public {} -// // cross-shard: collect and mint on different keys -// function test_execute_crossShard() public {} -// // cross-feed: collect and increase on different keys -// function test_execute_crossFeed() public {} -// } +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; +import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; +import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol"; +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; +import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol"; +import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; +import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; +import {PoolSwapTest} from "@uniswap/v4-core/src/test/PoolSwapTest.sol"; +import {LiquidityAmounts} from "../../contracts/libraries/LiquidityAmounts.sol"; +import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; +import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol"; + +import {IERC20} from "forge-std/interfaces/IERC20.sol"; +import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + +import {INonfungiblePositionManager, Actions} from "../../contracts/interfaces/INonfungiblePositionManager.sol"; +import {NonfungiblePositionManager} from "../../contracts/NonfungiblePositionManager.sol"; +import {LiquidityRange, LiquidityRangeId, LiquidityRangeIdLibrary} from "../../contracts/types/LiquidityRange.sol"; + +import {LiquidityFuzzers} from "../shared/fuzz/LiquidityFuzzers.sol"; + +import {LiquidityOperations} from "../shared/LiquidityOperations.sol"; +import {Planner} from "../utils/Planner.sol"; + +import "forge-std/console2.sol"; + +contract ExecuteTest is Test, Deployers, GasSnapshot, LiquidityFuzzers, LiquidityOperations { + using FixedPointMathLib for uint256; + using CurrencyLibrary for Currency; + using LiquidityRangeIdLibrary for LiquidityRange; + using PoolIdLibrary for PoolKey; + using SafeCast for uint256; + using Planner for Planner.Plan; + + PoolId poolId; + address alice = makeAddr("ALICE"); + address bob = makeAddr("BOB"); + + uint256 constant STARTING_USER_BALANCE = 10_000_000 ether; + + // expresses the fee as a wad (i.e. 3000 = 0.003e18 = 0.30%) + uint256 FEE_WAD; + + LiquidityRange range; + + function setUp() public { + Deployers.deployFreshManagerAndRouters(); + Deployers.deployMintAndApprove2Currencies(); + + (key, poolId) = initPool(currency0, currency1, IHooks(address(0)), 3000, SQRT_PRICE_1_1, ZERO_BYTES); + FEE_WAD = uint256(key.fee).mulDivDown(FixedPointMathLib.WAD, 1_000_000); + + lpm = new NonfungiblePositionManager(manager); + IERC20(Currency.unwrap(currency0)).approve(address(lpm), type(uint256).max); + IERC20(Currency.unwrap(currency1)).approve(address(lpm), type(uint256).max); + + // Give tokens to Alice and Bob, with approvals + IERC20(Currency.unwrap(currency0)).transfer(alice, STARTING_USER_BALANCE); + IERC20(Currency.unwrap(currency1)).transfer(alice, STARTING_USER_BALANCE); + IERC20(Currency.unwrap(currency0)).transfer(bob, STARTING_USER_BALANCE); + IERC20(Currency.unwrap(currency1)).transfer(bob, STARTING_USER_BALANCE); + vm.startPrank(alice); + IERC20(Currency.unwrap(currency0)).approve(address(lpm), type(uint256).max); + IERC20(Currency.unwrap(currency1)).approve(address(lpm), type(uint256).max); + vm.stopPrank(); + vm.startPrank(bob); + IERC20(Currency.unwrap(currency0)).approve(address(lpm), type(uint256).max); + IERC20(Currency.unwrap(currency1)).approve(address(lpm), type(uint256).max); + vm.stopPrank(); + + // define a reusable range + range = LiquidityRange({poolKey: key, tickLower: -300, tickUpper: 300}); + } + + function test_execute_increaseLiquidity_once(uint256 initialLiquidity, uint256 liquidityToAdd) public { + initialLiquidity = bound(initialLiquidity, 1e18, 1000e18); + liquidityToAdd = bound(liquidityToAdd, 1e18, 1000e18); + _mint(range, initialLiquidity, block.timestamp, address(this), ZERO_BYTES); + uint256 tokenId = lpm.nextTokenId() - 1; + + _increaseLiquidity(tokenId, liquidityToAdd, ZERO_BYTES, false); + + (,, uint256 liquidity,,,,) = lpm.positions(address(this), range.toId()); + assertEq(liquidity, initialLiquidity + liquidityToAdd); + } + + function test_execute_increaseLiquidity_twice( + uint256 initialiLiquidity, + uint256 liquidityToAdd, + uint256 liquidityToAdd2 + ) public { + initialiLiquidity = bound(initialiLiquidity, 1e18, 1000e18); + liquidityToAdd = bound(liquidityToAdd, 1e18, 1000e18); + liquidityToAdd2 = bound(liquidityToAdd2, 1e18, 1000e18); + _mint(range, initialiLiquidity, block.timestamp, address(this), ZERO_BYTES); + uint256 tokenId = lpm.nextTokenId() - 1; + + Planner.Plan memory planner = Planner.init(); + + planner = planner.add(Actions.INCREASE, abi.encode(tokenId, liquidityToAdd, ZERO_BYTES, false)); + planner = planner.add(Actions.INCREASE, abi.encode(tokenId, liquidityToAdd2, ZERO_BYTES, false)); + + console2.log("action"); + console2.log(uint8(planner.actions[0])); + console2.log(uint8(planner.actions[1])); + + Currency[] memory currencies = new Currency[](2); + currencies[0] = currency0; + currencies[1] = currency1; + lpm.modifyLiquidities(abi.encode(planner.actions, planner.params, currencies)); + + (,, uint256 liquidity,,,,) = lpm.positions(address(this), range.toId()); + assertEq(liquidity, initialiLiquidity + liquidityToAdd + liquidityToAdd2); + } + + // this case doesnt make sense in real world usage, so it doesnt have a cool name. but its a good test case + function test_execute_mintAndIncrease(uint256 initialLiquidity, uint256 liquidityToAdd) public { + initialLiquidity = bound(initialLiquidity, 1e18, 1000e18); + liquidityToAdd = bound(liquidityToAdd, 1e18, 1000e18); + + uint256 tokenId = 1; // assume that the .mint() produces tokenId=1, to be used in increaseLiquidity + + Planner.Plan memory planner = Planner.init(); + + planner = planner.add( + Actions.MINT, abi.encode(range, initialLiquidity, block.timestamp + 1, address(this), ZERO_BYTES) + ); + planner = planner.add(Actions.INCREASE, abi.encode(tokenId, liquidityToAdd, ZERO_BYTES, false)); + + Currency[] memory currencies = new Currency[](2); + currencies[0] = currency0; + currencies[1] = currency1; + lpm.modifyLiquidities(abi.encode(planner.actions, planner.params, currencies)); + + (,, uint256 liquidity,,,,) = lpm.positions(address(this), range.toId()); + assertEq(liquidity, initialLiquidity + liquidityToAdd); + } + + // rebalance: burn and mint + function test_execute_rebalance() public {} + // coalesce: burn and increase + function test_execute_coalesce() public {} + // split: decrease and mint + function test_execute_split() public {} + // shift: decrease and increase + function test_execute_shift() public {} + // shard: collect and mint + function test_execute_shard() public {} + // feed: collect and increase + function test_execute_feed() public {} + + // transplant: burn and mint on different keys + function test_execute_transplant() public {} + // cross-coalesce: burn and increase on different keys + function test_execute_crossCoalesce() public {} + // cross-split: decrease and mint on different keys + function test_execute_crossSplit() public {} + // cross-shift: decrease and increase on different keys + function test_execute_crossShift() public {} + // cross-shard: collect and mint on different keys + function test_execute_crossShard() public {} + // cross-feed: collect and increase on different keys + function test_execute_crossFeed() public {} +} diff --git a/test/position-managers/NonfungiblePositionManager.t.sol b/test/position-managers/NonfungiblePositionManager.t.sol index 7892f4b3..0b487e7a 100644 --- a/test/position-managers/NonfungiblePositionManager.t.sol +++ b/test/position-managers/NonfungiblePositionManager.t.sol @@ -30,8 +30,6 @@ import {LiquidityFuzzers} from "../shared/fuzz/LiquidityFuzzers.sol"; import {LiquidityOperations} from "../shared/LiquidityOperations.sol"; import {Planner} from "../utils/Planner.sol"; -import "forge-std/console2.sol"; - contract NonfungiblePositionManagerTest is Test, Deployers, GasSnapshot, LiquidityFuzzers, LiquidityOperations { using FixedPointMathLib for uint256; using CurrencyLibrary for Currency; diff --git a/test/utils/Planner.sol b/test/utils/Planner.sol index 302c0f83..df5a0aa5 100644 --- a/test/utils/Planner.sol +++ b/test/utils/Planner.sol @@ -18,8 +18,9 @@ library Planner { bytes[] memory params = new bytes[](plan.params.length + 1); for (uint256 i; i < actions.length - 1; i++) { - actions[i] = actions[i]; - params[i] = params[i]; + // Copy from plan. + actions[i] = plan.actions[i]; + params[i] = plan.params[i]; } actions[actions.length - 1] = action;