From d93b3c9237fe44e00a5c0f3bccffcce674af4119 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Mon, 11 Dec 2023 13:41:09 -0300 Subject: [PATCH 01/19] Remove access lock --- ...swap hook, already cached dynamic fee.snap | 2 +- .../cached dynamic fee, no hooks.snap | 2 +- .forge-snapshots/donate gas with 1 token.snap | 2 +- .../donate gas with 2 tokens.snap | 2 +- .forge-snapshots/initialize.snap | 2 +- .forge-snapshots/mint with empty hook.snap | 2 +- .forge-snapshots/mint with native token.snap | 2 +- .forge-snapshots/mint.snap | 2 +- .../mintWithEmptyHookEOAInitiated.snap | 2 +- .../modify position with noop.snap | 2 +- .../poolManager bytecode size.snap | 2 +- .forge-snapshots/simple swap with native.snap | 2 +- .forge-snapshots/simple swap.snap | 2 +- .forge-snapshots/simpleSwapEOAInitiated.snap | 2 +- .../simpleSwapNativeEOAInitiated.snap | 2 +- ...p against liquidity with native token.snap | 2 +- .forge-snapshots/swap against liquidity.snap | 2 +- .../swap burn claim for input.snap | 2 +- .../swap mint output as claim.snap | 2 +- .forge-snapshots/swap with dynamic fee.snap | 2 +- .forge-snapshots/swap with hooks.snap | 2 +- .forge-snapshots/swap with noop.snap | 2 +- .../update dynamic fee in before swap.snap | 2 +- src/PoolManager.sol | 1 - src/libraries/Hooks.sol | 5 +- src/test/AccessLockHook.sol | 280 ------ src/test/EmptyTestHooks.sol | 3 +- src/test/NoOpTestHooks.sol | 3 +- src/test/PoolDonateTest.sol | 12 +- src/test/PoolInitializeTest.sol | 14 +- src/test/PoolModifyPositionTest.sol | 15 +- src/test/PoolSwapTest.sol | 48 +- test/AccessLock.t.sol | 853 ------------------ test/Hooks.t.sol | 101 +-- 34 files changed, 80 insertions(+), 1301 deletions(-) delete mode 100644 src/test/AccessLockHook.sol delete mode 100644 test/AccessLock.t.sol diff --git a/.forge-snapshots/before swap hook, already cached dynamic fee.snap b/.forge-snapshots/before swap hook, already cached dynamic fee.snap index 44d36db5e..c6604068b 100644 --- a/.forge-snapshots/before swap hook, already cached dynamic fee.snap +++ b/.forge-snapshots/before swap hook, already cached dynamic fee.snap @@ -1 +1 @@ -194519 \ No newline at end of file +194810 \ No newline at end of file diff --git a/.forge-snapshots/cached dynamic fee, no hooks.snap b/.forge-snapshots/cached dynamic fee, no hooks.snap index ecc6dc509..7e5695e17 100644 --- a/.forge-snapshots/cached dynamic fee, no hooks.snap +++ b/.forge-snapshots/cached dynamic fee, no hooks.snap @@ -1 +1 @@ -146864 \ No newline at end of file +147155 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 1 token.snap b/.forge-snapshots/donate gas with 1 token.snap index d09eb4748..5124469c5 100644 --- a/.forge-snapshots/donate gas with 1 token.snap +++ b/.forge-snapshots/donate gas with 1 token.snap @@ -1 +1 @@ -137968 \ No newline at end of file +137830 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 2 tokens.snap b/.forge-snapshots/donate gas with 2 tokens.snap index ef8943ef6..750c16b17 100644 --- a/.forge-snapshots/donate gas with 2 tokens.snap +++ b/.forge-snapshots/donate gas with 2 tokens.snap @@ -1 +1 @@ -185390 \ No newline at end of file +185252 \ No newline at end of file diff --git a/.forge-snapshots/initialize.snap b/.forge-snapshots/initialize.snap index 1e8ddbbcd..680d86780 100644 --- a/.forge-snapshots/initialize.snap +++ b/.forge-snapshots/initialize.snap @@ -1 +1 @@ -74268 \ No newline at end of file +74213 \ No newline at end of file diff --git a/.forge-snapshots/mint with empty hook.snap b/.forge-snapshots/mint with empty hook.snap index f796eec15..b6a7df0e2 100644 --- a/.forge-snapshots/mint with empty hook.snap +++ b/.forge-snapshots/mint with empty hook.snap @@ -1 +1 @@ -323262 \ No newline at end of file +323124 \ No newline at end of file diff --git a/.forge-snapshots/mint with native token.snap b/.forge-snapshots/mint with native token.snap index 1817e9ba2..71f46d1e2 100644 --- a/.forge-snapshots/mint with native token.snap +++ b/.forge-snapshots/mint with native token.snap @@ -1 +1 @@ -200100 \ No newline at end of file +199962 \ No newline at end of file diff --git a/.forge-snapshots/mint.snap b/.forge-snapshots/mint.snap index ef066c96f..80422fda2 100644 --- a/.forge-snapshots/mint.snap +++ b/.forge-snapshots/mint.snap @@ -1 +1 @@ -200042 \ No newline at end of file +199904 \ No newline at end of file diff --git a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap index d22631db9..eb0cff75d 100644 --- a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap +++ b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap @@ -1 +1 @@ -255158 \ No newline at end of file +255020 \ No newline at end of file diff --git a/.forge-snapshots/modify position with noop.snap b/.forge-snapshots/modify position with noop.snap index 6ecd69a39..417bccb67 100644 --- a/.forge-snapshots/modify position with noop.snap +++ b/.forge-snapshots/modify position with noop.snap @@ -1 +1 @@ -58201 \ No newline at end of file +58063 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 533a9d3f2..c1302049f 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -23592 \ No newline at end of file +23544 \ No newline at end of file diff --git a/.forge-snapshots/simple swap with native.snap b/.forge-snapshots/simple swap with native.snap index e66e1bc95..d9d1efec5 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -195835 \ No newline at end of file +195697 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index e388485d4..2e51f4d43 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -204403 \ No newline at end of file +204265 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapEOAInitiated.snap b/.forge-snapshots/simpleSwapEOAInitiated.snap index 90ceff1c6..17442dc1e 100644 --- a/.forge-snapshots/simpleSwapEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapEOAInitiated.snap @@ -1 +1 @@ -175669 \ No newline at end of file +175531 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap index a32a609e5..73521e106 100644 --- a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap @@ -1 +1 @@ -172331 \ No newline at end of file +172193 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity with native token.snap b/.forge-snapshots/swap against liquidity with native token.snap index 3838ae2e8..419176d5f 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -126463 \ No newline at end of file +126325 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index 9ce2e3bf8..42b5a458b 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -113954 \ No newline at end of file +113816 \ No newline at end of file diff --git a/.forge-snapshots/swap burn claim for input.snap b/.forge-snapshots/swap burn claim for input.snap index 9390d2181..71512a224 100644 --- a/.forge-snapshots/swap burn claim for input.snap +++ b/.forge-snapshots/swap burn claim for input.snap @@ -1 +1 @@ -133127 \ No newline at end of file +132989 \ No newline at end of file diff --git a/.forge-snapshots/swap mint output as claim.snap b/.forge-snapshots/swap mint output as claim.snap index 12d1a218c..6b4d61781 100644 --- a/.forge-snapshots/swap mint output as claim.snap +++ b/.forge-snapshots/swap mint output as claim.snap @@ -1 +1 @@ -216596 \ No newline at end of file +216458 \ No newline at end of file diff --git a/.forge-snapshots/swap with dynamic fee.snap b/.forge-snapshots/swap with dynamic fee.snap index da2a04f8e..a3be9998f 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -193620 \ No newline at end of file +193911 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index 54570960b..9fffbdfcd 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -113932 \ No newline at end of file +113794 \ No newline at end of file diff --git a/.forge-snapshots/swap with noop.snap b/.forge-snapshots/swap with noop.snap index 876b81885..ae6e82e41 100644 --- a/.forge-snapshots/swap with noop.snap +++ b/.forge-snapshots/swap with noop.snap @@ -1 +1 @@ -51546 \ No newline at end of file +51408 \ No newline at end of file diff --git a/.forge-snapshots/update dynamic fee in before swap.snap b/.forge-snapshots/update dynamic fee in before swap.snap index 4d31f4eaa..bafcf12e7 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -200363 \ No newline at end of file +200654 \ No newline at end of file diff --git a/src/PoolManager.sol b/src/PoolManager.sol index e0c30663c..14f57bbbe 100644 --- a/src/PoolManager.sol +++ b/src/PoolManager.sol @@ -100,7 +100,6 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, Claims { function _checkLocker(address caller, address locker, IHooks hook) internal pure { if (caller == locker) return; - if (caller == address(hook) && hook.hasPermission(Hooks.ACCESS_LOCK_FLAG)) return; revert LockedBy(locker, address(hook)); } diff --git a/src/libraries/Hooks.sol b/src/libraries/Hooks.sol index 09c68e608..5627b7bda 100644 --- a/src/libraries/Hooks.sol +++ b/src/libraries/Hooks.sol @@ -25,7 +25,6 @@ library Hooks { uint256 internal constant BEFORE_DONATE_FLAG = 1 << 153; uint256 internal constant AFTER_DONATE_FLAG = 1 << 152; uint256 internal constant NO_OP_FLAG = 1 << 151; - uint256 internal constant ACCESS_LOCK_FLAG = 1 << 150; bytes4 public constant NO_OP_SELECTOR = bytes4(keccak256(abi.encodePacked("NoOp"))); @@ -39,7 +38,6 @@ library Hooks { bool beforeDonate; bool afterDonate; bool noOp; - bool accessLock; } /// @notice Thrown if the address will not lead to the specified hook calls being called @@ -67,7 +65,6 @@ library Hooks { || permissions.beforeDonate != self.hasPermission(BEFORE_DONATE_FLAG) || permissions.afterDonate != self.hasPermission(AFTER_DONATE_FLAG) || permissions.noOp != self.hasPermission(NO_OP_FLAG) - || permissions.accessLock != self.hasPermission(ACCESS_LOCK_FLAG) ) { revert HookAddressNotValid(address(self)); } @@ -87,7 +84,7 @@ library Hooks { // If a hook contract is set, it must have at least 1 flag set, or have a dynamic fee return address(hook) == address(0) ? !fee.isDynamicFee() - : (uint160(address(hook)) >= ACCESS_LOCK_FLAG || fee.isDynamicFee()); + : (uint160(address(hook)) >= NO_OP_FLAG || fee.isDynamicFee()); } /// @notice performs a hook call using the given calldata on the given hook diff --git a/src/test/AccessLockHook.sol b/src/test/AccessLockHook.sol deleted file mode 100644 index 8293af2fb..000000000 --- a/src/test/AccessLockHook.sol +++ /dev/null @@ -1,280 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; - -import {BaseTestHooks} from "./BaseTestHooks.sol"; -import {PoolKey} from "../types/PoolKey.sol"; -import {IPoolManager} from "../interfaces/IPoolManager.sol"; -import {IHooks} from "../interfaces/IHooks.sol"; -import {CurrencyLibrary, Currency} from "../types/Currency.sol"; -import {Hooks} from "../libraries/Hooks.sol"; -import {TickMath} from "../libraries/TickMath.sol"; -import {Test} from "forge-std/Test.sol"; -import {ILockCallback} from "../interfaces/callback/ILockCallback.sol"; -import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {Constants} from "../../test/utils/Constants.sol"; -import {PoolIdLibrary} from "../types/PoolId.sol"; -import {BalanceDelta} from "../types/BalanceDelta.sol"; - -contract AccessLockHook is Test, BaseTestHooks { - using PoolIdLibrary for PoolKey; - using CurrencyLibrary for Currency; - - IPoolManager manager; - - constructor(IPoolManager _manager) { - manager = _manager; - } - - error InvalidAction(); - - enum LockAction { - Mint, - Take, - Donate, - Swap, - ModifyPosition, - Burn, - Settle, - Initialize, - NoOp - } - - function beforeInitialize( - address, /* sender **/ - PoolKey calldata key, - uint160, /* sqrtPriceX96 **/ - bytes calldata hookData - ) external override returns (bytes4) { - return _executeAction(key, hookData, IHooks.beforeInitialize.selector); - } - - function beforeSwap( - address, /* sender **/ - PoolKey calldata key, - IPoolManager.SwapParams calldata, /* params **/ - bytes calldata hookData - ) external override returns (bytes4) { - return _executeAction(key, hookData, IHooks.beforeSwap.selector); - } - - function beforeDonate( - address, /* sender **/ - PoolKey calldata key, - uint256, /* amount0 **/ - uint256, /* amount1 **/ - bytes calldata hookData - ) external override returns (bytes4) { - return _executeAction(key, hookData, IHooks.beforeDonate.selector); - } - - function beforeModifyPosition( - address, /* sender **/ - PoolKey calldata key, - IPoolManager.ModifyPositionParams calldata, /* params **/ - bytes calldata hookData - ) external override returns (bytes4) { - return _executeAction(key, hookData, IHooks.beforeModifyPosition.selector); - } - - function _executeAction(PoolKey memory key, bytes calldata hookData, bytes4 selector) internal returns (bytes4) { - if (hookData.length == 0) { - // We have re-entered the hook or we are initializing liquidity in the pool before testing the lock actions. - return selector; - } - (uint256 amount, LockAction action) = abi.decode(hookData, (uint256, LockAction)); - - // These actions just use some hardcoded parameters. - if (action == LockAction.Mint) { - manager.mint(key.currency1, address(this), amount); - } else if (action == LockAction.Take) { - manager.take(key.currency1, address(this), amount); - } else if (action == LockAction.Donate) { - manager.donate(key, amount, amount, new bytes(0)); - } else if (action == LockAction.Swap) { - manager.swap( - key, - IPoolManager.SwapParams({ - zeroForOne: true, - amountSpecified: int256(amount), - sqrtPriceLimitX96: TickMath.MIN_SQRT_RATIO + 1 - }), - new bytes(0) - ); - } else if (action == LockAction.ModifyPosition) { - manager.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -60, tickUpper: 60, liquidityDelta: int256(amount)}), - new bytes(0) - ); - } else if (action == LockAction.NoOp) { - assertEq(address(manager.getCurrentHook()), address(this)); - return Hooks.NO_OP_SELECTOR; - } else if (action == LockAction.Burn) { - manager.burn(key.currency1, amount); - } else if (action == LockAction.Settle) { - manager.take(key.currency1, address(this), amount); - assertEq(MockERC20(Currency.unwrap(key.currency1)).balanceOf(address(this)), amount); - assertEq(manager.getLockNonzeroDeltaCount(), 1); - MockERC20(Currency.unwrap(key.currency1)).transfer(address(manager), amount); - manager.settle(key.currency1); - assertEq(manager.getLockNonzeroDeltaCount(), 0); - } else if (action == LockAction.Initialize) { - PoolKey memory newKey = PoolKey({ - currency0: key.currency0, - currency1: key.currency1, - fee: Constants.FEE_LOW, - tickSpacing: 60, - hooks: IHooks(address(0)) - }); - manager.initialize(newKey, Constants.SQRT_RATIO_1_2, new bytes(0)); - } else { - revert InvalidAction(); - } - - return selector; - } -} - -// Hook that can access the lock. -// Also has the ability to call out to another hook or pool. -contract AccessLockHook2 is Test, BaseTestHooks { - IPoolManager manager; - - error IncorrectHookSet(); - - constructor(IPoolManager _manager) { - manager = _manager; - } - - function beforeModifyPosition( - address sender, - PoolKey calldata key, - IPoolManager.ModifyPositionParams calldata params, - bytes calldata hookData - ) external override returns (bytes4) { - if (address(manager.getCurrentHook()) != address(this)) { - revert IncorrectHookSet(); - } - - (bool shouldCallHook, PoolKey memory key2) = abi.decode(hookData, (bool, PoolKey)); - - if (shouldCallHook) { - // Should revert. - bytes memory hookData2 = abi.encode(100, AccessLockHook.LockAction.Mint); - IHooks(key2.hooks).beforeModifyPosition(sender, key, params, hookData2); // params dont really matter, just want to tell the other hook to do a mint action, but will revert - } else { - // Should succeed and should NOT set the current hook to key2.hooks. - // The permissions should remain to THIS hook during this lock. - manager.modifyPosition(key2, params, new bytes(0)); - - if (address(manager.getCurrentHook()) != address(this)) { - revert IncorrectHookSet(); - } - // Should succeed. - manager.mint(key.currency1, address(this), 10); - } - return IHooks.beforeModifyPosition.selector; - } -} - -// Reenters the PoolManager to donate and asserts currentHook is set and unset correctly throughout the popping and pushing of locks. -contract AccessLockHook3 is Test, ILockCallback, BaseTestHooks { - IPoolManager manager; - // The pool to donate to in the nested lock. - // Ensure this has balance of currency0.abi - PoolKey key; - - constructor(IPoolManager _manager) { - manager = _manager; - } - - // Instead of passing through key all the way to the nested lock, just save it. - function setKey(PoolKey memory _key) external { - key = _key; - } - - function beforeModifyPosition( - address, /* sender **/ - PoolKey calldata, /* key **/ - IPoolManager.ModifyPositionParams calldata, /* params **/ - bytes calldata /* hookData **/ - ) external override returns (bytes4) { - assertEq(address(manager.getCurrentHook()), address(this)); - manager.lock(address(this), abi.encode(true)); - assertEq(address(manager.getCurrentHook()), address(this)); - manager.lock(address(this), abi.encode(false)); - assertEq(address(manager.getCurrentHook()), address(this)); - return IHooks.beforeModifyPosition.selector; - } - - function lockAcquired(address caller, bytes memory data) external returns (bytes memory) { - require(caller == address(this)); - assertEq(manager.getLockLength(), 2); - assertEq(address(manager.getCurrentHook()), address(0)); - - (bool isFirstLock) = abi.decode(data, (bool)); - if (isFirstLock) { - manager.donate(key, 10, 0, new bytes(0)); - assertEq(address(manager.getCurrentHook()), address(key.hooks)); - MockERC20(Currency.unwrap(key.currency0)).transfer(address(manager), 10); - manager.settle(key.currency0); - } - return data; - } -} - -contract AccessLockFeeHook is Test, BaseTestHooks { - IPoolManager manager; - - uint256 constant WITHDRAWAL_FEE_BIPS = 40; // 40/10000 = 0.4% - uint256 constant SWAP_FEE_BIPS = 55; // 55/10000 = 0.55% - uint256 constant TOTAL_BIPS = 10000; - - constructor(IPoolManager _manager) { - manager = _manager; - } - - function afterModifyPosition( - address, /* sender **/ - PoolKey calldata key, - IPoolManager.ModifyPositionParams calldata, /* params **/ - BalanceDelta delta, - bytes calldata /* hookData **/ - ) external override returns (bytes4) { - int128 amount0 = delta.amount0(); - int128 amount1 = delta.amount1(); - - // positive delta => user owes money => liquidity addition - if (amount0 >= 0 && amount1 >= 0) return IHooks.afterModifyPosition.selector; - - // negative delta => user is owed money => liquidity withdrawal - uint256 amount0Fee = uint128(-amount0) * WITHDRAWAL_FEE_BIPS / TOTAL_BIPS; - uint256 amount1Fee = uint128(-amount1) * WITHDRAWAL_FEE_BIPS / TOTAL_BIPS; - - manager.take(key.currency0, address(this), amount0Fee); - manager.take(key.currency1, address(this), amount1Fee); - - return IHooks.afterModifyPosition.selector; - } - - function afterSwap( - address, /* sender **/ - PoolKey calldata key, - IPoolManager.SwapParams calldata params, - BalanceDelta delta, - bytes calldata /* hookData **/ - ) external override returns (bytes4) { - int128 amount0 = delta.amount0(); - int128 amount1 = delta.amount1(); - - // fee on output token - output delta will be negative - (Currency feeCurrency, uint256 outputAmount) = - (params.zeroForOne) ? (key.currency1, uint128(-amount1)) : (key.currency0, uint128(-amount0)); - - uint256 feeAmount = outputAmount * SWAP_FEE_BIPS / TOTAL_BIPS; - - manager.take(feeCurrency, address(this), feeAmount); - - return IHooks.afterSwap.selector; - } -} diff --git a/src/test/EmptyTestHooks.sol b/src/test/EmptyTestHooks.sol index 6e8f9fc3f..1bfbe8097 100644 --- a/src/test/EmptyTestHooks.sol +++ b/src/test/EmptyTestHooks.sol @@ -21,8 +21,7 @@ contract EmptyTestHooks is IHooks { afterSwap: true, beforeDonate: true, afterDonate: true, - noOp: true, - accessLock: true + noOp: true }) ); } diff --git a/src/test/NoOpTestHooks.sol b/src/test/NoOpTestHooks.sol index 5c5dd8a1d..4aa6dc241 100644 --- a/src/test/NoOpTestHooks.sol +++ b/src/test/NoOpTestHooks.sol @@ -20,8 +20,7 @@ contract NoOpTestHooks is BaseTestHooks { afterSwap: false, beforeDonate: true, afterDonate: false, - noOp: true, - accessLock: false + noOp: true }) ); } diff --git a/src/test/PoolDonateTest.sol b/src/test/PoolDonateTest.sol index efbbd6176..a6b2eac42 100644 --- a/src/test/PoolDonateTest.sol +++ b/src/test/PoolDonateTest.sol @@ -59,13 +59,11 @@ contract PoolDonateTest is PoolTestBase, Test { (,, uint256 reserveAfter0, int256 deltaAfter0) = _fetchBalances(data.key.currency0, data.sender); (,, uint256 reserveAfter1, int256 deltaAfter1) = _fetchBalances(data.key.currency1, data.sender); - if (!data.key.hooks.hasPermission(Hooks.ACCESS_LOCK_FLAG)) { - assertEq(reserveBefore0, reserveAfter0); - assertEq(reserveBefore1, reserveAfter1); - if (!data.key.hooks.hasPermission(Hooks.NO_OP_FLAG)) { - assertEq(deltaAfter0, int256(data.amount0)); - assertEq(deltaAfter1, int256(data.amount1)); - } + assertEq(reserveBefore0, reserveAfter0); + assertEq(reserveBefore1, reserveAfter1); + if (!data.key.hooks.hasPermission(Hooks.NO_OP_FLAG)) { + assertEq(deltaAfter0, int256(data.amount0)); + assertEq(deltaAfter1, int256(data.amount1)); } if (delta == BalanceDeltaLibrary.MAXIMUM_DELTA) { diff --git a/src/test/PoolInitializeTest.sol b/src/test/PoolInitializeTest.sol index 772ae5a85..081a1293a 100644 --- a/src/test/PoolInitializeTest.sol +++ b/src/test/PoolInitializeTest.sol @@ -44,17 +44,9 @@ contract PoolInitializeTest is Test, PoolTestBase { int256 delta1 = manager.currencyDelta(address(this), data.key.currency1); uint256 nonZeroDC = manager.getLockNonzeroDeltaCount(); - if (!data.key.hooks.hasPermission(Hooks.ACCESS_LOCK_FLAG)) { - assertEq(delta0, 0, "delta0"); - assertEq(delta1, 0, "delta1"); - assertEq(nonZeroDC, 0, "NonzeroDeltaCount"); - } else { - // settle deltas - if (delta0 > 0) _settle(data.key.currency0, data.sender, int128(delta0), true); - if (delta1 > 0) _settle(data.key.currency1, data.sender, int128(delta1), true); - if (delta0 < 0) _take(data.key.currency0, data.sender, int128(delta0), true); - if (delta1 < 0) _take(data.key.currency1, data.sender, int128(delta1), true); - } + assertEq(delta0, 0, "delta0"); + assertEq(delta1, 0, "delta1"); + assertEq(nonZeroDC, 0, "NonzeroDeltaCount"); return abi.encode(tick); } diff --git a/src/test/PoolModifyPositionTest.sol b/src/test/PoolModifyPositionTest.sol index 0a5852529..866768298 100644 --- a/src/test/PoolModifyPositionTest.sol +++ b/src/test/PoolModifyPositionTest.sol @@ -52,15 +52,12 @@ contract PoolModifyPositionTest is Test, PoolTestBase { (,,, int256 delta0) = _fetchBalances(data.key.currency0, data.sender); (,,, int256 delta1) = _fetchBalances(data.key.currency1, data.sender); - // These assertions only apply in non lock-accessing pools. - if (!data.key.hooks.hasPermission(Hooks.ACCESS_LOCK_FLAG)) { - if (data.params.liquidityDelta > 0) { - assert(delta0 > 0 || delta1 > 0 || data.key.hooks.hasPermission(Hooks.NO_OP_FLAG)); - assert(!(delta0 < 0 || delta1 < 0)); - } else { - assert(delta0 < 0 || delta1 < 0 || data.key.hooks.hasPermission(Hooks.NO_OP_FLAG)); - assert(!(delta0 > 0 || delta1 > 0)); - } + if (data.params.liquidityDelta > 0) { + assert(delta0 > 0 || delta1 > 0 || data.key.hooks.hasPermission(Hooks.NO_OP_FLAG)); + assert(!(delta0 < 0 || delta1 < 0)); + } else { + assert(delta0 < 0 || delta1 < 0 || data.key.hooks.hasPermission(Hooks.NO_OP_FLAG)); + assert(!(delta0 > 0 || delta1 > 0)); } if (delta0 > 0) _settle(data.key.currency0, data.sender, int128(delta0), true); diff --git a/src/test/PoolSwapTest.sol b/src/test/PoolSwapTest.sol index 872720be3..8e568466f 100644 --- a/src/test/PoolSwapTest.sol +++ b/src/test/PoolSwapTest.sol @@ -68,33 +68,29 @@ contract PoolSwapTest is Test, PoolTestBase { (,, uint256 reserveAfter0, int256 deltaAfter0) = _fetchBalances(data.key.currency0, data.sender); (,, uint256 reserveAfter1, int256 deltaAfter1) = _fetchBalances(data.key.currency1, data.sender); - if (!data.key.hooks.hasPermission(Hooks.ACCESS_LOCK_FLAG)) { - // Hanndle assertions when the hook cannot access the lock. - // IE if the hook can access the lock, the reserves before and after are not necessarily the same. Hook can "take". - assertEq(reserveBefore0, reserveAfter0); - assertEq(reserveBefore1, reserveAfter1); - - if (!data.key.hooks.hasPermission(Hooks.NO_OP_FLAG)) { - if (data.params.zeroForOne) { - if (data.params.amountSpecified > 0) { - // exact input, 0 for 1 - assertEq(deltaAfter0, data.params.amountSpecified); - assert(deltaAfter1 < 0); - } else { - // exact output, 0 for 1 - assert(deltaAfter0 > 0); - assertEq(deltaAfter1, data.params.amountSpecified); - } + assertEq(reserveBefore0, reserveAfter0); + assertEq(reserveBefore1, reserveAfter1); + + if (!data.key.hooks.hasPermission(Hooks.NO_OP_FLAG)) { + if (data.params.zeroForOne) { + if (data.params.amountSpecified > 0) { + // exact input, 0 for 1 + assertEq(deltaAfter0, data.params.amountSpecified); + assert(deltaAfter1 < 0); } else { - if (data.params.amountSpecified > 0) { - // exact input, 1 for 0 - assertEq(deltaAfter1, data.params.amountSpecified); - assert(deltaAfter0 < 0); - } else { - // exact output, 1 for 0 - assert(deltaAfter1 > 0); - assertEq(deltaAfter0, data.params.amountSpecified); - } + // exact output, 0 for 1 + assert(deltaAfter0 > 0); + assertEq(deltaAfter1, data.params.amountSpecified); + } + } else { + if (data.params.amountSpecified > 0) { + // exact input, 1 for 0 + assertEq(deltaAfter1, data.params.amountSpecified); + assert(deltaAfter0 < 0); + } else { + // exact output, 1 for 0 + assert(deltaAfter1 > 0); + assertEq(deltaAfter0, data.params.amountSpecified); } } } diff --git a/test/AccessLock.t.sol b/test/AccessLock.t.sol deleted file mode 100644 index 82ea2270f..000000000 --- a/test/AccessLock.t.sol +++ /dev/null @@ -1,853 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; - -import {Test} from "forge-std/Test.sol"; -import {AccessLockHook, AccessLockHook2, AccessLockHook3, AccessLockFeeHook} from "../src/test/AccessLockHook.sol"; -import {IPoolManager} from "../src/interfaces/IPoolManager.sol"; -import {PoolModifyPositionTest} from "../src/test/PoolModifyPositionTest.sol"; -import {PoolSwapTest} from "../src/test/PoolSwapTest.sol"; -import {PoolDonateTest} from "../src/test/PoolDonateTest.sol"; -import {Constants} from "./utils/Constants.sol"; -import {PoolKey} from "../src/types/PoolKey.sol"; -import {Deployers} from "./utils/Deployers.sol"; -import {Currency, CurrencyLibrary} from "../src/types/Currency.sol"; -import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {Hooks} from "../src/libraries/Hooks.sol"; -import {IHooks} from "../src/interfaces/IHooks.sol"; -import {BalanceDelta, BalanceDeltaLibrary} from "../src/types/BalanceDelta.sol"; -import {Pool} from "../src/libraries/Pool.sol"; -import {TickMath} from "../src/libraries/TickMath.sol"; -import {PoolIdLibrary} from "../src/types/PoolId.sol"; - -contract AccessLockTest is Test, Deployers { - using Pool for Pool.State; - using CurrencyLibrary for Currency; - using PoolIdLibrary for PoolKey; - using BalanceDeltaLibrary for BalanceDelta; - - AccessLockHook accessLockHook; - AccessLockHook noAccessLockHook; - AccessLockHook2 accessLockHook2; - AccessLockHook3 accessLockHook3; - AccessLockHook accessLockNoOpHook; - AccessLockFeeHook accessLockFeeHook; - - // global for stack too deep errors - BalanceDelta delta; - - uint128 amount = 1e18; - - function setUp() public { - // Initialize managers and routers. - deployFreshManagerAndRouters(); - (currency0, currency1) = deployMintAndApprove2Currencies(); - - // Create AccessLockHook. - address accessLockAddress = address( - uint160( - Hooks.ACCESS_LOCK_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG - | Hooks.BEFORE_DONATE_FLAG - ) - ); - deployCodeTo("AccessLockHook.sol:AccessLockHook", abi.encode(manager), accessLockAddress); - accessLockHook = AccessLockHook(accessLockAddress); - - (key,) = - initPool(currency0, currency1, IHooks(accessLockAddress), Constants.FEE_MEDIUM, SQRT_RATIO_1_1, ZERO_BYTES); - - // Create AccessLockHook2. - address accessLockAddress2 = address(uint160(Hooks.ACCESS_LOCK_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG)); - deployCodeTo("AccessLockHook.sol:AccessLockHook2", abi.encode(manager), accessLockAddress2); - accessLockHook2 = AccessLockHook2(accessLockAddress2); - - // Create AccessLockHook3. - address accessLockAddress3 = address( - (uint160(makeAddr("hook3")) << 10) >> 10 - | (uint160(Hooks.ACCESS_LOCK_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG)) - ); - deployCodeTo("AccessLockHook.sol:AccessLockHook3", abi.encode(manager), accessLockAddress3); - accessLockHook3 = AccessLockHook3(accessLockAddress3); - - // Create NoAccessLockHook. - address noAccessLockHookAddress = address(uint160(Hooks.BEFORE_MODIFY_POSITION_FLAG)); - deployCodeTo("AccessLockHook.sol:AccessLockHook", abi.encode(manager), noAccessLockHookAddress); - noAccessLockHook = AccessLockHook(noAccessLockHookAddress); - - // Create AccessLockHook with NoOp. - address accessLockNoOpHookAddress = address( - uint160( - Hooks.NO_OP_FLAG | Hooks.ACCESS_LOCK_FLAG | Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG - | Hooks.BEFORE_MODIFY_POSITION_FLAG | Hooks.BEFORE_DONATE_FLAG - ) - ); - deployCodeTo("AccessLockHook.sol:AccessLockHook", abi.encode(manager), accessLockNoOpHookAddress); - accessLockNoOpHook = AccessLockHook(accessLockNoOpHookAddress); - - // Create AccessLockFeeHook - address accessLockFeeHookAddress = - address(uint160(Hooks.ACCESS_LOCK_FLAG | Hooks.AFTER_SWAP_FLAG | Hooks.AFTER_MODIFY_POSITION_FLAG)); - deployCodeTo("AccessLockHook.sol:AccessLockFeeHook", abi.encode(manager), accessLockFeeHookAddress); - accessLockFeeHook = AccessLockFeeHook(accessLockFeeHookAddress); - } - - function test_onlyByLocker_revertsForNoAccessLockPool() public { - (PoolKey memory keyWithoutAccessLockFlag,) = - initPool(currency0, currency1, IHooks(noAccessLockHook), Constants.FEE_MEDIUM, SQRT_RATIO_1_1, ZERO_BYTES); - - vm.expectRevert( - abi.encodeWithSelector( - IPoolManager.LockedBy.selector, address(modifyPositionRouter), address(noAccessLockHook) - ) - ); - modifyPositionRouter.modifyPosition( - keyWithoutAccessLockFlag, - IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 0}), - abi.encode(10, AccessLockHook.LockAction.Mint) // attempts a mint action that should revert - ); - } - /** - * - * - * The following test suite tests that appropriate hooks can call - * every function gated by the `onlyByLocker` modifier. - * We call these "LockActions". - * LockActions: - * - Mint - * - Take - * - Swap - * - ModifyPosition - * - Donate - * - Burn - * - Settle - * - Initialize - * Each of these calls is then tested from every callback after the - * currentHook gets set (beforeModifyPosition, beforeSwap, and beforeDonate). - * - */ - - /** - * - * BEFORE MODIFY POSITION TESTS - * - */ - function test_beforeModifyPosition_mint_succeedsWithAccessLock() public { - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - delta = modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams(0, 60, 1e18), abi.encode(amount, AccessLockHook.LockAction.Mint) - ); - - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - assertEq(balanceOfBefore0 - balanceOfAfter0, uint256(uint128(delta.amount0()))); - // The balance of our contract should be from the modifyPositionRouter (delta) AND the hook (amount). - assertEq(balanceOfBefore1 - balanceOfAfter1, uint256(amount + uint256(uint128(delta.amount1())))); - - assertEq(manager.balanceOf(address(accessLockHook), currency1), amount); - } - - function test_beforeModifyPosition_take_succeedsWithAccessLock() public { - // Add liquidity so there is something to take. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - // Hook only takes currency 1 rn. - delta = modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams(-60, 60, 1e18), abi.encode(amount, AccessLockHook.LockAction.Take) - ); - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - assertEq(balanceOfBefore0 - balanceOfAfter0, uint256(uint128(delta.amount0()))); - // The balance of our contract should be from the modifyPositionRouter (delta) AND the hook (amount). - assertEq(balanceOfBefore1 - balanceOfAfter1, uint256(amount + uint256(uint128(delta.amount1())))); - assertEq(MockERC20(Currency.unwrap(currency1)).balanceOf(address(accessLockHook)), amount); - } - - function test_beforeModifyPosition_swap_succeedsWithAccessLock() public { - // Add liquidity so there is something to swap over. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - // Essentially "no-op"s the modifyPosition call and executes a swap before hand, applying the deltas from the swap to the locker. - modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams(-120, 120, 0), abi.encode(amount, AccessLockHook.LockAction.Swap) - ); - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - // Balance decreases because we are swapping currency0 for currency1. - assertLt(balanceOfAfter0, balanceOfBefore0); - // Balance should be greater in currency1. - assertGt(balanceOfAfter1, balanceOfBefore1); - } - - function test_beforeModifyPosition_modifyPosition_succeedsWithAccessLock() public { - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams(-120, 120, 1e18), - abi.encode(amount, AccessLockHook.LockAction.ModifyPosition) - ); - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - // Should have less balance in both currencies. - assertLt(balanceOfAfter0, balanceOfBefore0); - assertLt(balanceOfAfter1, balanceOfBefore1); - } - - function test_beforeModifyPosition_donate_succeedsWithAccessLock() public { - // Add liquidity so there is a position to receive fees. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams(-120, 120, 1e18), - abi.encode(amount, AccessLockHook.LockAction.Donate) - ); - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - // Should have less balance in both currencies. - assertLt(balanceOfAfter0, balanceOfBefore0); - assertLt(balanceOfAfter1, balanceOfBefore1); - } - - function test_beforeModifyPosition_burn_succeedsWithAccessLock() public { - // Add liquidity so there is a position to swap over. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - delta = swapRouter.swap( - key, - IPoolManager.SwapParams(true, 10000, TickMath.MIN_SQRT_RATIO + 1), - PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true, currencyAlreadySent: false}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 amount1 = uint256(uint128(-delta.amount1())); - // We have some balance in the manager. - assertEq(manager.balanceOf(address(this), currency1), amount1); - manager.transfer(address(key.hooks), currency1, amount1); - assertEq(manager.balanceOf(address(key.hooks), currency1), amount1); - - modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams(-120, 120, 0), abi.encode(amount1, AccessLockHook.LockAction.Burn) - ); - - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - assertEq(balanceOfAfter1, balanceOfBefore1 + amount1); - } - - function test_beforeModifyPosition_settle_succeedsWithAccessLock() public { - // Add liquidity so there is something to take. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - // Assertions in the hook. Takes and then settles within the hook. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams(-120, 120, 1e18), - abi.encode(amount, AccessLockHook.LockAction.Settle) - ); - } - - function test_beforeModifyPosition_initialize_succeedsWithAccessLock() public { - // The hook intitializes a new pool with the new key at Constants.SQRT_RATIO_1_2; - modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams(-120, 120, 1e18), abi.encode(0, AccessLockHook.LockAction.Initialize) - ); - - PoolKey memory newKey = PoolKey({ - currency0: key.currency0, - currency1: key.currency1, - fee: Constants.FEE_LOW, - tickSpacing: 60, - hooks: IHooks(address(0)) - }); - (Pool.Slot0 memory slot0,,,) = manager.pools(newKey.toId()); - - assertEq(slot0.sqrtPriceX96, Constants.SQRT_RATIO_1_2); - } - - /** - * - * BEFORE SWAP TESTS - * - */ - - function test_beforeSwap_mint_succeedsWithAccessLock() public { - // Add liquidity so there is something to swap against. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - // Small amount to swap (like NoOp). This way we can expect balances to just be from the hook applied delta. - delta = swapRouter.swap( - key, - IPoolManager.SwapParams(true, 1, TickMath.MIN_SQRT_RATIO + 1), - PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}), - abi.encode(amount, AccessLockHook.LockAction.Mint) - ); - - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - assertEq(balanceOfBefore0 - balanceOfAfter0, uint256(uint128(delta.amount0()))); - // The balance of our contract should be from the modifyPositionRouter (delta) AND the hook (amount). - assertEq(balanceOfBefore1 - balanceOfAfter1, uint256(amount + uint256(uint128(delta.amount1())))); - - assertEq(manager.balanceOf(address(accessLockHook), currency1), amount); - } - - function test_beforeSwap_take_succeedsWithAccessLock() public { - // Add liquidity so there is something to take. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - // Hook only takes currency 1 rn. - // Use small amount to NoOp. - delta = swapRouter.swap( - key, - IPoolManager.SwapParams(true, 1, TickMath.MIN_SQRT_RATIO + 1), - PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}), - abi.encode(amount, AccessLockHook.LockAction.Take) - ); - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - assertEq(balanceOfBefore0 - balanceOfAfter0, uint256(uint128(delta.amount0()))); - // The balance of our contract should be from the modifyPositionRouter (delta) AND the hook (amount). - assertEq(balanceOfBefore1 - balanceOfAfter1, uint256(amount + uint256(uint128(delta.amount1())))); - assertEq(MockERC20(Currency.unwrap(currency1)).balanceOf(address(accessLockHook)), amount); - } - - function test_beforeSwap_swap_succeedsWithAccessLock() public { - // Add liquidity so there is something to swap over. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - swapRouter.swap( - key, - // Use small amounts so that the zeroForOne swap is larger - IPoolManager.SwapParams(false, 1, TickMath.MAX_SQRT_RATIO - 1), - PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}), - abi.encode(amount, AccessLockHook.LockAction.Swap) - ); - - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - // The larger swap is zeroForOne - // Balance decreases because we are swapping currency0 for currency1. - assertLt(balanceOfAfter0, balanceOfBefore0); - // Balance should be greater in currency1. - assertGt(balanceOfAfter1, balanceOfBefore1); - } - - function test_beforeSwap_modifyPosition_succeedsWithAccessLock() public { - // Add liquidity so there is something to swap over. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - // Make the swap amount small (like a NoOp). - swapRouter.swap( - key, - IPoolManager.SwapParams(true, 1, TickMath.MIN_SQRT_RATIO + 1), - PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}), - abi.encode(amount, AccessLockHook.LockAction.ModifyPosition) - ); - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - // Should have less balance in both currencies. - assertLt(balanceOfAfter0, balanceOfBefore0); - assertLt(balanceOfAfter1, balanceOfBefore1); - } - - function test_beforeSwap_donate_succeedsWithAccessLock() public { - // Add liquidity so there is a position to receive fees. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - // Make the swap amount small (like a NoOp). - swapRouter.swap( - key, - IPoolManager.SwapParams(true, 1, TickMath.MIN_SQRT_RATIO + 1), - PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}), - abi.encode(amount, AccessLockHook.LockAction.Donate) - ); - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - // Should have less balance in both currencies. - assertLt(balanceOfAfter0, balanceOfBefore0); - assertLt(balanceOfAfter1, balanceOfBefore1); - } - - /** - * - * BEFORE DONATE TESTS - * - */ - - function test_beforeDonate_mint_succeedsWithAccessLock() public { - // Add liquidity so there is something to donate to. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - delta = donateRouter.donate(key, 1e18, 1e18, abi.encode(amount, AccessLockHook.LockAction.Mint)); - - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - assertEq(balanceOfBefore0 - balanceOfAfter0, uint256(uint128(delta.amount0()))); - // The balance of our contract should be from the donateRouter (delta) AND the hook (amount). - assertEq(balanceOfBefore1 - balanceOfAfter1, uint256(amount + uint256(uint128(delta.amount1())))); - - assertEq(manager.balanceOf(address(accessLockHook), currency1), amount); - } - - function test_beforeDonate_take_succeedsWithAccessLock() public { - // Add liquidity so there is something to take. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - // Hook only takes currency 1 rn. - delta = donateRouter.donate(key, 1e18, 1e18, abi.encode(amount, AccessLockHook.LockAction.Take)); - // Take applies a positive delta in currency1. - // Donate applies a positive delta in currency0 and currency1. - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - assertEq(balanceOfBefore0 - balanceOfAfter0, uint256(uint128(delta.amount0()))); - // The balance of our contract should be from the modifyPositionRouter (delta) AND the hook (amount). - assertEq(balanceOfBefore1 - balanceOfAfter1, uint256(amount + uint256(uint128(delta.amount1())))); - assertEq(MockERC20(Currency.unwrap(currency1)).balanceOf(address(accessLockHook)), amount); - } - - function test_beforeDonate_swap_succeedsWithAccessLock() public { - // Add liquidity so there is something to swap over. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - // Donate small amounts (NoOp) so we know the swap amount dominates. - donateRouter.donate(key, 1, 1, abi.encode(amount, AccessLockHook.LockAction.Swap)); - - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - // Balance of currency0 decreases bc we 1) donate and 2) swap zeroForOne. - assertLt(balanceOfAfter0, balanceOfBefore0); - // Since the donate amount is small, and we swapped zeroForOne, we expect balance of currency1 to increase. - assertGt(balanceOfAfter1, balanceOfBefore1); - } - - function test_beforeDonate_modifyPosition_succeedsWithAccessLock() public { - // Add liquidity so there is something to donate to. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - donateRouter.donate(key, 1e18, 1e18, abi.encode(amount, AccessLockHook.LockAction.ModifyPosition)); - - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - // Should have less balance in both currencies from adding liquidity AND donating. - assertLt(balanceOfAfter0, balanceOfBefore0); - assertLt(balanceOfAfter1, balanceOfBefore1); - } - - function test_beforeDonate_donate_succeedsWithAccessLock() public { - // Add liquidity so there is a position to receive fees. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - // Make the swap amount small (like a NoOp). - donateRouter.donate(key, 1e18, 1e18, abi.encode(amount, AccessLockHook.LockAction.Donate)); - - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - // Should have less balance in both currencies. - assertLt(balanceOfAfter0, balanceOfBefore0); - assertLt(balanceOfAfter1, balanceOfBefore1); - } - /** - * - * BEFORE INITIALIZE TESTS - * - */ - - function test_beforeInitialize_mint_succeedsWithAccessLock() public { - PoolKey memory key1 = PoolKey({ - currency0: currency0, - currency1: currency1, - fee: Constants.FEE_MEDIUM, - tickSpacing: 60, - hooks: IHooks(address(accessLockNoOpHook)) - }); - - initializeRouter.initialize(key1, SQRT_RATIO_1_1, abi.encode(amount, AccessLockHook.LockAction.Mint)); - - assertEq(manager.balanceOf(address(accessLockNoOpHook), currency1), amount); - } - - function test_beforeInitialize_take_succeedsWithAccessLock() public { - PoolKey memory key1 = PoolKey({ - currency0: currency0, - currency1: currency1, - fee: Constants.FEE_MEDIUM, - tickSpacing: 60, - hooks: IHooks(address(accessLockNoOpHook)) - }); - - // Add liquidity to a different pool there is something to take. - modifyPositionRouter.modifyPosition( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1000e18}), - ZERO_BYTES - ); - - initializeRouter.initialize(key1, SQRT_RATIO_1_1, abi.encode(amount, AccessLockHook.LockAction.Take)); - - assertEq(MockERC20(Currency.unwrap(currency1)).balanceOf(address(accessLockNoOpHook)), amount); - } - - function test_beforeInitialize_swap_revertsOnPoolNotInitialized() public { - PoolKey memory key1 = PoolKey({ - currency0: currency0, - currency1: currency1, - fee: Constants.FEE_MEDIUM, - tickSpacing: 60, - hooks: IHooks(address(accessLockNoOpHook)) - }); - - vm.expectRevert(IPoolManager.PoolNotInitialized.selector); - initializeRouter.initialize(key1, SQRT_RATIO_1_1, abi.encode(amount, AccessLockHook.LockAction.Swap)); - } - - function test_beforeInitialize_modifyPosition_revertsOnPoolNotInitialized() public { - PoolKey memory key1 = PoolKey({ - currency0: currency0, - currency1: currency1, - fee: Constants.FEE_MEDIUM, - tickSpacing: 60, - hooks: IHooks(address(accessLockNoOpHook)) - }); - - vm.expectRevert(IPoolManager.PoolNotInitialized.selector); - initializeRouter.initialize(key1, SQRT_RATIO_1_1, abi.encode(amount, AccessLockHook.LockAction.ModifyPosition)); - } - - function test_beforeInitialize_donate_revertsOnPoolNotInitialized() public { - PoolKey memory key1 = PoolKey({ - currency0: currency0, - currency1: currency1, - fee: Constants.FEE_MEDIUM, - tickSpacing: 60, - hooks: IHooks(address(accessLockNoOpHook)) - }); - - vm.expectRevert(IPoolManager.PoolNotInitialized.selector); - initializeRouter.initialize(key1, SQRT_RATIO_1_1, abi.encode(amount, AccessLockHook.LockAction.Donate)); - } - - /** - * - * HOOK FEE TESTS - * - */ - - function test_hookFees_takesFeeOnWithdrawal() public { - (key,) = initPool( - currency0, currency1, IHooks(address(accessLockFeeHook)), Constants.FEE_MEDIUM, SQRT_RATIO_1_1, ZERO_BYTES - ); - - (uint256 userBalanceBefore0, uint256 poolBalanceBefore0, uint256 reservesBefore0) = _fetchBalances(currency0); - (uint256 userBalanceBefore1, uint256 poolBalanceBefore1, uint256 reservesBefore1) = _fetchBalances(currency1); - - // add liquidity - delta = modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); - - (uint256 userBalanceAfter0, uint256 poolBalanceAfter0, uint256 reservesAfter0) = _fetchBalances(currency0); - (uint256 userBalanceAfter1, uint256 poolBalanceAfter1, uint256 reservesAfter1) = _fetchBalances(currency1); - - assert(delta.amount0() > 0 && delta.amount1() > 0); - assertEq(userBalanceBefore0 - uint128(delta.amount0()), userBalanceAfter0, "addLiq user balance currency0"); - assertEq(userBalanceBefore1 - uint128(delta.amount1()), userBalanceAfter1, "addLiq user balance currency1"); - assertEq(poolBalanceBefore0 + uint128(delta.amount0()), poolBalanceAfter0, "addLiq pool balance currency0"); - assertEq(poolBalanceBefore1 + uint128(delta.amount1()), poolBalanceAfter1, "addLiq pool balance currency1"); - assertEq(reservesBefore0 + uint128(delta.amount0()), reservesAfter0, "addLiq reserves currency0"); - assertEq(reservesBefore1 + uint128(delta.amount1()), reservesAfter1, "addLiq reserves currency1"); - - (userBalanceBefore0, poolBalanceBefore0, reservesBefore0) = - (userBalanceAfter0, poolBalanceAfter0, reservesAfter0); - (userBalanceBefore1, poolBalanceBefore1, reservesBefore1) = - (userBalanceAfter1, poolBalanceAfter1, reservesAfter1); - - // remove liquidity, a 40 bip fee should be taken - LIQ_PARAMS.liquidityDelta *= -1; - delta = modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); - - (userBalanceAfter0, poolBalanceAfter0, reservesAfter0) = _fetchBalances(currency0); - (userBalanceAfter1, poolBalanceAfter1, reservesAfter1) = _fetchBalances(currency1); - - assert(delta.amount0() < 0 && delta.amount1() < 0); - - uint256 totalWithdraw0 = uint128(-delta.amount0()) - (uint128(-delta.amount0()) * 40 / 10000); - uint256 totalWithdraw1 = uint128(-delta.amount1()) - (uint128(-delta.amount1()) * 40 / 10000); - - assertEq(userBalanceBefore0 + totalWithdraw0, userBalanceAfter0, "removeLiq user balance currency0"); - assertEq(userBalanceBefore1 + totalWithdraw1, userBalanceAfter1, "removeLiq user balance currency1"); - assertEq(poolBalanceBefore0 - uint128(-delta.amount0()), poolBalanceAfter0, "removeLiq pool balance currency0"); - assertEq(poolBalanceBefore1 - uint128(-delta.amount1()), poolBalanceAfter1, "removeLiq pool balance currency1"); - assertEq(reservesBefore0 - uint128(-delta.amount0()), reservesAfter0, "removeLiq reserves currency0"); - assertEq(reservesBefore1 - uint128(-delta.amount1()), reservesAfter1, "removeLiq reserves currency1"); - } - - function test_hookFees_takesFeeOnInputOfSwap() public { - (key,) = initPool( - currency0, currency1, IHooks(address(accessLockFeeHook)), Constants.FEE_MEDIUM, SQRT_RATIO_1_1, ZERO_BYTES - ); - - // add liquidity - delta = modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); - - // now swap, with a hook fee of 55 bips - (uint256 userBalanceBefore0, uint256 poolBalanceBefore0, uint256 reservesBefore0) = _fetchBalances(currency0); - (uint256 userBalanceBefore1, uint256 poolBalanceBefore1, uint256 reservesBefore1) = _fetchBalances(currency1); - - delta = swapRouter.swap( - key, - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100000, sqrtPriceLimitX96: SQRT_RATIO_1_2}), - PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}), - ZERO_BYTES - ); - - assert(delta.amount0() > 0 && delta.amount1() < 0); - - uint256 amountIn0 = uint128(delta.amount0()); - uint256 userAmountOut1 = uint128(-delta.amount1()) - (uint128(-delta.amount1()) * 55 / 10000); - - (uint256 userBalanceAfter0, uint256 poolBalanceAfter0, uint256 reservesAfter0) = _fetchBalances(currency0); - (uint256 userBalanceAfter1, uint256 poolBalanceAfter1, uint256 reservesAfter1) = _fetchBalances(currency1); - - assertEq(userBalanceBefore0 - amountIn0, userBalanceAfter0, "swap user balance currency0"); - assertEq(userBalanceBefore1 + userAmountOut1, userBalanceAfter1, "swap user balance currency1"); - assertEq(poolBalanceBefore0 + amountIn0, poolBalanceAfter0, "swap pool balance currency0"); - assertEq(poolBalanceBefore1 - uint128(-delta.amount1()), poolBalanceAfter1, "swap pool balance currency1"); - assertEq(reservesBefore0 + amountIn0, reservesAfter0, "swap reserves currency0"); - assertEq(reservesBefore1 - uint128(-delta.amount1()), reservesAfter1, "swap reserves currency1"); - } - - /** - * - * EDGE CASE TESTS - * - */ - - function test_onlyByLocker_revertsWhenHookIsNotCurrentHook() public { - // Call first access lock hook. Should succeed. - uint256 balanceOfBefore1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - uint256 balanceOfBefore0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - - delta = modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams(0, 60, 1e18), abi.encode(amount, AccessLockHook.LockAction.Mint) - ); - - uint256 balanceOfAfter0 = MockERC20(Currency.unwrap(currency0)).balanceOf(address(this)); - uint256 balanceOfAfter1 = MockERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - - assertEq(balanceOfBefore0 - balanceOfAfter0, uint256(uint128(delta.amount0()))); - // The balance of our contract should be from the modifyPositionRouter (delta) AND the hook (amount). - assertEq(balanceOfBefore1 - balanceOfAfter1, uint256(amount + uint256(uint128(delta.amount1())))); - - assertEq(manager.balanceOf(address(accessLockHook), currency1), amount); - - assertEq(address(manager.getCurrentHook()), address(0)); - - (PoolKey memory keyAccessLockHook2,) = - initPool(currency0, currency1, IHooks(accessLockHook2), Constants.FEE_MEDIUM, SQRT_RATIO_1_1, ZERO_BYTES); - - // Delegates the beforeModifyPosition call to the hook in `key` which tries to mint on manager - // but reverts because hook in `key` is not the current hook. - vm.expectRevert( - abi.encodeWithSelector( - IPoolManager.LockedBy.selector, address(modifyPositionRouter), address(accessLockHook2) - ) - ); - delta = modifyPositionRouter.modifyPosition( - keyAccessLockHook2, IPoolManager.ModifyPositionParams(0, 60, 1e18), abi.encode(true, key) - ); - } - - function test_onlyByLocker_succeedsAfterHookMakesNestedCall() public { - (PoolKey memory keyWithNoHook,) = - initPool(currency0, currency1, IHooks(address(0)), Constants.FEE_MEDIUM, SQRT_RATIO_1_1, ZERO_BYTES); - - (PoolKey memory keyAccessLockHook2,) = - initPool(currency0, currency1, IHooks(accessLockHook2), Constants.FEE_MEDIUM, SQRT_RATIO_1_1, ZERO_BYTES); - - modifyPositionRouter.modifyPosition( - keyAccessLockHook2, IPoolManager.ModifyPositionParams(0, 60, 1e18), abi.encode(false, keyWithNoHook) - ); - assertEq(manager.balanceOf(address(accessLockHook2), currency1), 10); - } - - function test_onlyByLocker_revertsWhenThereIsNoOutsideLock() public { - modifyPositionRouter.modifyPosition(key, IPoolManager.ModifyPositionParams(0, 60, 1e18), ZERO_BYTES); - assertEq(address(manager.getCurrentHook()), address(0)); - - vm.expectRevert(abi.encodeWithSelector(IPoolManager.LockedBy.selector, address(0), address(0))); - vm.prank(address(key.hooks)); - manager.modifyPosition(key, IPoolManager.ModifyPositionParams(0, 60, 1e18), ZERO_BYTES); - } - - function test_getCurrentHook_isClearedAfterNestedLock() public { - // Create pool for AccessLockHook3. - (PoolKey memory keyAccessLockHook3,) = - initPool(currency0, currency1, IHooks(accessLockHook3), Constants.FEE_MEDIUM, SQRT_RATIO_1_1, ZERO_BYTES); - // Fund AccessLockHook3 with currency0. - MockERC20(Currency.unwrap(currency0)).transfer(address(accessLockHook3), 10); - assertEq(MockERC20(Currency.unwrap(currency0)).balanceOf(address(accessLockHook3)), 10); - - // Create pool to donate 10 of currency0 to inside of AccessLockHook3. This means AccessLockHook3 must acquire a new lock and settle. - // The currentHook addresses are checked inside this nested lock. - (PoolKey memory _key,) = - initPool(currency0, currency1, IHooks(address(0)), Constants.FEE_MEDIUM, SQRT_RATIO_1_1, ZERO_BYTES); - // Add liquidity so that the AccessLockHook3 can donate to something. - modifyPositionRouter.modifyPosition(_key, IPoolManager.ModifyPositionParams(-60, 60, 10 * 10 ** 18), ZERO_BYTES); - accessLockHook3.setKey(_key); - - // Asserts are in the AccessLockHook3. - modifyPositionRouter.modifyPosition( - keyAccessLockHook3, IPoolManager.ModifyPositionParams(0, 60, 1e18), ZERO_BYTES - ); - } - - function test_getCurrentHook_isClearedAfterNoOpOnAllHooks() public { - (PoolKey memory noOpKey,) = - initPool(currency0, currency1, IHooks(accessLockNoOpHook), Constants.FEE_MEDIUM, SQRT_RATIO_1_1, ZERO_BYTES); - - // Assertions for current hook address in AccessLockHook and respective routers. - // beforeModifyPosition noOp - modifyPositionRouter.modifyPosition( - noOpKey, - IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 0}), - abi.encode(0, AccessLockHook.LockAction.NoOp) - ); - - // beforeDonate noOp - donateRouter.donate(noOpKey, 1e18, 1e18, abi.encode(0, AccessLockHook.LockAction.NoOp)); - - // beforeSwap noOp - swapRouter.swap( - noOpKey, - IPoolManager.SwapParams(true, 1, TickMath.MIN_SQRT_RATIO + 1), - PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}), - abi.encode(0, AccessLockHook.LockAction.NoOp) - ); - } - - function _fetchBalances(Currency currency) - internal - view - returns (uint256 userBalance, uint256 poolBalance, uint256 reserves) - { - userBalance = currency.balanceOf(address(this)); - poolBalance = currency.balanceOf(address(manager)); - reserves = manager.reservesOf(currency); - } -} diff --git a/test/Hooks.t.sol b/test/Hooks.t.sol index b6cc5d12a..5bc970ed1 100644 --- a/test/Hooks.t.sol +++ b/test/Hooks.t.sol @@ -19,7 +19,6 @@ import {Deployers} from "./utils/Deployers.sol"; import {Fees} from "../src/Fees.sol"; import {PoolId, PoolIdLibrary} from "../src/types/PoolId.sol"; import {PoolKey} from "../src/types/PoolKey.sol"; -import {AccessLockHook} from "../src/test/AccessLockHook.sol"; import {IERC20Minimal} from "../src/interfaces/external/IERC20Minimal.sol"; import {BalanceDelta} from "../src/types/BalanceDelta.sol"; @@ -151,8 +150,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -164,7 +162,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressBeforeInitialize(uint160 addr) public { @@ -182,8 +179,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertTrue(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -195,7 +191,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressAfterInitialize(uint160 addr) public { @@ -213,8 +208,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -226,7 +220,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressBeforeAndAfterInitialize(uint160 addr) public { @@ -243,8 +236,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertTrue(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -256,7 +248,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressBeforeModify(uint160 addr) public { @@ -273,8 +264,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -286,7 +276,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressAfterModify(uint160 addr) public { @@ -303,8 +292,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -316,7 +304,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressBeforeAndAfterModify(uint160 addr) public { @@ -334,8 +321,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -347,7 +333,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressBeforeInitializeAfterModify(uint160 addr) public { @@ -365,8 +350,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertTrue(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -378,7 +362,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressBeforeSwap(uint160 addr) public { @@ -395,8 +378,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -408,7 +390,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressAfterSwap(uint160 addr) public { @@ -425,8 +406,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: true, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -438,7 +418,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressBeforeAndAfterSwap(uint160 addr) public { @@ -455,8 +434,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: true, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -468,7 +446,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressBeforeDonate(uint160 addr) public { @@ -485,8 +462,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: true, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -498,7 +474,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertTrue(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressAfterDonate(uint160 addr) public { @@ -515,8 +490,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: false, afterDonate: true, - noOp: false, - accessLock: false + noOp: false }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -528,7 +502,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertTrue(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressBeforeAndAfterDonate(uint160 addr) public { @@ -545,8 +518,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: true, afterDonate: true, - noOp: false, - accessLock: false + noOp: false }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -558,37 +530,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertTrue(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertTrue(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); - } - - function test_validateHookAddress_accessLock(uint160 addr) public { - uint160 preAddr = uint160(uint256(addr) & clearAllHookPermisssionsMask); - IHooks hookAddr = IHooks(address(uint160(preAddr | Hooks.ACCESS_LOCK_FLAG))); - Hooks.validateHookPermissions( - hookAddr, - Hooks.Permissions({ - beforeInitialize: false, - afterInitialize: false, - beforeModifyPosition: false, - afterModifyPosition: false, - beforeSwap: false, - afterSwap: false, - beforeDonate: false, - afterDonate: false, - noOp: false, - accessLock: true - }) - ); - assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.AFTER_INITIALIZE_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.BEFORE_MODIFY_POSITION_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.AFTER_MODIFY_POSITION_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.BEFORE_SWAP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.AFTER_SWAP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertTrue(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressAllHooks(uint160 addr) public { @@ -606,8 +547,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: true, beforeDonate: true, afterDonate: true, - noOp: true, - accessLock: true + noOp: true }) ); assertTrue(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -619,7 +559,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertTrue(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertTrue(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertTrue(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertTrue(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressNoOp(uint160 addr) public { @@ -643,8 +582,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: true, afterDonate: false, - noOp: true, - accessLock: false + noOp: true }) ); assertFalse(hookAddr.hasPermission(Hooks.BEFORE_INITIALIZE_FLAG)); @@ -656,7 +594,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertTrue(hookAddr.hasPermission(Hooks.BEFORE_DONATE_FLAG)); assertFalse(hookAddr.hasPermission(Hooks.AFTER_DONATE_FLAG)); assertTrue(hookAddr.hasPermission(Hooks.NO_OP_FLAG)); - assertFalse(hookAddr.hasPermission(Hooks.ACCESS_LOCK_FLAG)); } function testValidateHookAddressFailsAllHooks(uint152 addr, uint8 mask) public { @@ -675,8 +612,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: true, beforeDonate: true, afterDonate: true, - noOp: true, - accessLock: true + noOp: true }) ); } @@ -698,8 +634,7 @@ contract HooksTest is Test, Deployers, GasSnapshot { afterSwap: false, beforeDonate: false, afterDonate: false, - noOp: false, - accessLock: false + noOp: false }) ); } From 13642d8d06dc481ae718de971ea318d3be4a4155 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Mon, 11 Dec 2023 13:48:55 -0300 Subject: [PATCH 02/19] remove current hook --- ...swap hook, already cached dynamic fee.snap | 2 +- .../cached dynamic fee, no hooks.snap | 2 +- .forge-snapshots/donate gas with 1 token.snap | 2 +- .../donate gas with 2 tokens.snap | 2 +- .forge-snapshots/initialize.snap | 2 +- .forge-snapshots/mint with empty hook.snap | 2 +- .forge-snapshots/mint with native token.snap | 2 +- .forge-snapshots/mint.snap | 2 +- .../mintWithEmptyHookEOAInitiated.snap | 2 +- .../modify position with noop.snap | 2 +- .../poolManager bytecode size.snap | 2 +- .forge-snapshots/simple swap with native.snap | 2 +- .forge-snapshots/simple swap.snap | 2 +- .forge-snapshots/simpleSwapEOAInitiated.snap | 2 +- .../simpleSwapNativeEOAInitiated.snap | 2 +- ...p against liquidity with native token.snap | 2 +- .forge-snapshots/swap against liquidity.snap | 2 +- .../swap burn claim for input.snap | 2 +- .../swap mint output as claim.snap | 2 +- .forge-snapshots/swap with dynamic fee.snap | 2 +- .forge-snapshots/swap with hooks.snap | 2 +- .forge-snapshots/swap with noop.snap | 2 +- .../update dynamic fee in before swap.snap | 2 +- src/PoolManager.sol | 10 ++-- src/interfaces/IPoolManager.sol | 6 +-- src/libraries/Hooks.sol | 5 -- src/libraries/Lockers.sol | 23 ---------- src/test/PoolDonateTest.sol | 3 -- src/test/PoolModifyPositionTest.sol | 2 - src/test/PoolSwapTest.sol | 3 -- test/CurrentHookAddress.t.sol | 46 ------------------- 31 files changed, 27 insertions(+), 117 deletions(-) delete mode 100644 test/CurrentHookAddress.t.sol diff --git a/.forge-snapshots/before swap hook, already cached dynamic fee.snap b/.forge-snapshots/before swap hook, already cached dynamic fee.snap index c6604068b..a51e4dc7d 100644 --- a/.forge-snapshots/before swap hook, already cached dynamic fee.snap +++ b/.forge-snapshots/before swap hook, already cached dynamic fee.snap @@ -1 +1 @@ -194810 \ No newline at end of file +190172 \ No newline at end of file diff --git a/.forge-snapshots/cached dynamic fee, no hooks.snap b/.forge-snapshots/cached dynamic fee, no hooks.snap index 7e5695e17..116e65dbf 100644 --- a/.forge-snapshots/cached dynamic fee, no hooks.snap +++ b/.forge-snapshots/cached dynamic fee, no hooks.snap @@ -1 +1 @@ -147155 \ No newline at end of file +144157 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 1 token.snap b/.forge-snapshots/donate gas with 1 token.snap index 5124469c5..1a6d6c7ee 100644 --- a/.forge-snapshots/donate gas with 1 token.snap +++ b/.forge-snapshots/donate gas with 1 token.snap @@ -1 +1 @@ -137830 \ No newline at end of file +135379 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 2 tokens.snap b/.forge-snapshots/donate gas with 2 tokens.snap index 750c16b17..2a457206c 100644 --- a/.forge-snapshots/donate gas with 2 tokens.snap +++ b/.forge-snapshots/donate gas with 2 tokens.snap @@ -1 +1 @@ -185252 \ No newline at end of file +182276 \ No newline at end of file diff --git a/.forge-snapshots/initialize.snap b/.forge-snapshots/initialize.snap index 680d86780..4da269400 100644 --- a/.forge-snapshots/initialize.snap +++ b/.forge-snapshots/initialize.snap @@ -1 +1 @@ -74213 \ No newline at end of file +73620 \ No newline at end of file diff --git a/.forge-snapshots/mint with empty hook.snap b/.forge-snapshots/mint with empty hook.snap index b6a7df0e2..49fa9cf5c 100644 --- a/.forge-snapshots/mint with empty hook.snap +++ b/.forge-snapshots/mint with empty hook.snap @@ -1 +1 @@ -323124 \ No newline at end of file +316830 \ No newline at end of file diff --git a/.forge-snapshots/mint with native token.snap b/.forge-snapshots/mint with native token.snap index 71f46d1e2..ee8d8fc5e 100644 --- a/.forge-snapshots/mint with native token.snap +++ b/.forge-snapshots/mint with native token.snap @@ -1 +1 @@ -199962 \ No newline at end of file +196948 \ No newline at end of file diff --git a/.forge-snapshots/mint.snap b/.forge-snapshots/mint.snap index 80422fda2..b57aba5d1 100644 --- a/.forge-snapshots/mint.snap +++ b/.forge-snapshots/mint.snap @@ -1 +1 @@ -199904 \ No newline at end of file +196890 \ No newline at end of file diff --git a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap index eb0cff75d..e66edc99e 100644 --- a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap +++ b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap @@ -1 +1 @@ -255020 \ No newline at end of file +249251 \ No newline at end of file diff --git a/.forge-snapshots/modify position with noop.snap b/.forge-snapshots/modify position with noop.snap index 417bccb67..7fbc53aff 100644 --- a/.forge-snapshots/modify position with noop.snap +++ b/.forge-snapshots/modify position with noop.snap @@ -1 +1 @@ -58063 \ No newline at end of file +54459 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index c1302049f..2bb5ec788 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -23544 \ No newline at end of file +23211 \ No newline at end of file diff --git a/.forge-snapshots/simple swap with native.snap b/.forge-snapshots/simple swap with native.snap index d9d1efec5..53c9731f5 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -195697 \ No newline at end of file +192699 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index 2e51f4d43..324b12693 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -204265 \ No newline at end of file +201267 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapEOAInitiated.snap b/.forge-snapshots/simpleSwapEOAInitiated.snap index 17442dc1e..1cd5f151e 100644 --- a/.forge-snapshots/simpleSwapEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapEOAInitiated.snap @@ -1 +1 @@ -175531 \ No newline at end of file +172533 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap index 73521e106..4787f4032 100644 --- a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap @@ -1 +1 @@ -172193 \ No newline at end of file +169195 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity with native token.snap b/.forge-snapshots/swap against liquidity with native token.snap index 419176d5f..a7f63ed6b 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -126325 \ No newline at end of file +123327 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index 42b5a458b..99e49a2b9 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -113816 \ No newline at end of file +110818 \ No newline at end of file diff --git a/.forge-snapshots/swap burn claim for input.snap b/.forge-snapshots/swap burn claim for input.snap index 71512a224..f34da438c 100644 --- a/.forge-snapshots/swap burn claim for input.snap +++ b/.forge-snapshots/swap burn claim for input.snap @@ -1 +1 @@ -132989 \ No newline at end of file +129969 \ No newline at end of file diff --git a/.forge-snapshots/swap mint output as claim.snap b/.forge-snapshots/swap mint output as claim.snap index 6b4d61781..e58b14bf3 100644 --- a/.forge-snapshots/swap mint output as claim.snap +++ b/.forge-snapshots/swap mint output as claim.snap @@ -1 +1 @@ -216458 \ No newline at end of file +213460 \ No newline at end of file diff --git a/.forge-snapshots/swap with dynamic fee.snap b/.forge-snapshots/swap with dynamic fee.snap index a3be9998f..4fb6d0ab1 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -193911 \ No newline at end of file +189273 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index 9fffbdfcd..716127b41 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -113794 \ No newline at end of file +110796 \ No newline at end of file diff --git a/.forge-snapshots/swap with noop.snap b/.forge-snapshots/swap with noop.snap index ae6e82e41..c9c77bffb 100644 --- a/.forge-snapshots/swap with noop.snap +++ b/.forge-snapshots/swap with noop.snap @@ -1 +1 @@ -51408 \ No newline at end of file +47842 \ No newline at end of file diff --git a/.forge-snapshots/update dynamic fee in before swap.snap b/.forge-snapshots/update dynamic fee in before swap.snap index bafcf12e7..9c08b20d9 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -200654 \ No newline at end of file +195993 \ No newline at end of file diff --git a/src/PoolManager.sol b/src/PoolManager.sol index 14f57bbbe..3ed315e3d 100644 --- a/src/PoolManager.sol +++ b/src/PoolManager.sol @@ -94,13 +94,13 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, Claims { /// @notice This will revert if a function is called by any address other than the current locker OR the most recently called, pre-permissioned hook. modifier onlyByLocker() { - _checkLocker(msg.sender, Lockers.getCurrentLocker(), Lockers.getCurrentHook()); + _checkLocker(msg.sender, Lockers.getCurrentLocker()); _; } - function _checkLocker(address caller, address locker, IHooks hook) internal pure { + function _checkLocker(address caller, address locker) internal pure { if (caller == locker) return; - revert LockedBy(locker, address(hook)); + revert LockedBy(locker); } /// @inheritdoc IPoolManager @@ -345,10 +345,6 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, Claims { return Lockers.nonzeroDeltaCount(); } - function getCurrentHook() external view returns (IHooks) { - return Lockers.getCurrentHook(); - } - function getPoolTickInfo(PoolId id, int24 tick) external view returns (Pool.TickInfo memory) { return pools[id].getPoolTickInfo(tick); } diff --git a/src/interfaces/IPoolManager.sol b/src/interfaces/IPoolManager.sol index c8d7998ec..82142f7fe 100644 --- a/src/interfaces/IPoolManager.sol +++ b/src/interfaces/IPoolManager.sol @@ -23,8 +23,7 @@ interface IPoolManager is IFees, IClaims { /// @notice Thrown when a function is called by an address that is not the current locker /// @param locker The current locker - /// @param currentHook The most recently called hook - error LockedBy(address locker, address currentHook); + error LockedBy(address locker); /// @notice The ERC1155 being deposited is not the Uniswap ERC1155 error NotPoolManagerToken(); @@ -119,9 +118,6 @@ interface IPoolManager is IFees, IClaims { /// @notice Returns the length of the lockers array, which is the number of locks open on the PoolManager. function getLockLength() external view returns (uint256 _length); - /// @notice Returns the most recently called hook. - function getCurrentHook() external view returns (IHooks _currentHook); - /// @notice Returns the number of nonzero deltas open on the PoolManager that must be zerod by the close of the initial lock. function getLockNonzeroDeltaCount() external view returns (uint256 _nonzeroDeltaCount); diff --git a/src/libraries/Hooks.sol b/src/libraries/Hooks.sol index 5627b7bda..361bbbb67 100644 --- a/src/libraries/Hooks.sol +++ b/src/libraries/Hooks.sol @@ -91,8 +91,6 @@ library Hooks { /// @return expectedSelector The selector that the hook is expected to return /// @return selector The selector that the hook actually returned function _callHook(IHooks self, bytes memory data) private returns (bytes4 expectedSelector, bytes4 selector) { - bool set = Lockers.setCurrentHook(self); - assembly { expectedSelector := mload(add(data, 0x20)) } @@ -101,9 +99,6 @@ library Hooks { if (!success) _revert(result); selector = abi.decode(result, (bytes4)); - - // We only want to clear the current hook if it was set in setCurrentHook in this execution frame. - if (set) Lockers.clearCurrentHook(); } /// @notice performs a hook call using the given calldata on the given hook diff --git a/src/libraries/Lockers.sol b/src/libraries/Lockers.sol index fff26695b..43e60842c 100644 --- a/src/libraries/Lockers.sol +++ b/src/libraries/Lockers.sol @@ -121,33 +121,10 @@ library Lockers { } } - function getCurrentHook() internal view returns (IHooks currentHook) { - return IHooks(getHook(length())); - } - function getHook(uint256 i) internal view returns (address hook) { uint256 slot = HOOK_ADDRESS_SLOT + i; assembly { hook := tload(slot) } } - - function setCurrentHook(IHooks currentHook) internal returns (bool set) { - // Set the hook address for the current locker if the address is 0. - // If the address is nonzero, a hook has already been set for this lock, and is not allowed to be updated or cleared at the end of the call. - if (address(getCurrentHook()) == address(0)) { - uint256 slot = HOOK_ADDRESS_SLOT + length(); - assembly { - tstore(slot, currentHook) - } - return true; - } - } - - function clearCurrentHook() internal { - uint256 slot = HOOK_ADDRESS_SLOT + length(); - assembly { - tstore(slot, 0) - } - } } diff --git a/src/test/PoolDonateTest.sol b/src/test/PoolDonateTest.sol index a6b2eac42..726104d6e 100644 --- a/src/test/PoolDonateTest.sol +++ b/src/test/PoolDonateTest.sol @@ -53,9 +53,6 @@ contract PoolDonateTest is PoolTestBase, Test { BalanceDelta delta = manager.donate(data.key, data.amount0, data.amount1, data.hookData); - // Checks that the current hook is cleared if there is an access lock. Note that if this router is ever used in a nested lock this will fail. - assertEq(address(manager.getCurrentHook()), address(0)); - (,, uint256 reserveAfter0, int256 deltaAfter0) = _fetchBalances(data.key.currency0, data.sender); (,, uint256 reserveAfter1, int256 deltaAfter1) = _fetchBalances(data.key.currency1, data.sender); diff --git a/src/test/PoolModifyPositionTest.sol b/src/test/PoolModifyPositionTest.sol index 866768298..6480730f5 100644 --- a/src/test/PoolModifyPositionTest.sol +++ b/src/test/PoolModifyPositionTest.sol @@ -46,8 +46,6 @@ contract PoolModifyPositionTest is Test, PoolTestBase { CallbackData memory data = abi.decode(rawData, (CallbackData)); BalanceDelta delta = manager.modifyPosition(data.key, data.params, data.hookData); - // Checks that the current hook is cleared if there is an access lock. Note that if this router is ever used in a nested lock this will fail. - assertEq(address(manager.getCurrentHook()), address(0)); (,,, int256 delta0) = _fetchBalances(data.key.currency0, data.sender); (,,, int256 delta1) = _fetchBalances(data.key.currency1, data.sender); diff --git a/src/test/PoolSwapTest.sol b/src/test/PoolSwapTest.sol index 8e568466f..01715c6b1 100644 --- a/src/test/PoolSwapTest.sol +++ b/src/test/PoolSwapTest.sol @@ -62,9 +62,6 @@ contract PoolSwapTest is Test, PoolTestBase { BalanceDelta delta = manager.swap(data.key, data.params, data.hookData); - // Checks that the current hook is cleared if there is an access lock. Note that if this router is ever used in a nested lock this will fail. - assertEq(address(manager.getCurrentHook()), address(0)); - (,, uint256 reserveAfter0, int256 deltaAfter0) = _fetchBalances(data.key.currency0, data.sender); (,, uint256 reserveAfter1, int256 deltaAfter1) = _fetchBalances(data.key.currency1, data.sender); diff --git a/test/CurrentHookAddress.t.sol b/test/CurrentHookAddress.t.sol deleted file mode 100644 index 591027474..000000000 --- a/test/CurrentHookAddress.t.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; - -import {Test} from "forge-std/Test.sol"; -import {Lockers} from "../src/libraries/Lockers.sol"; -import {IHooks} from "../src/interfaces/IHooks.sol"; - -contract CurrentHookAddressTest is Test { - function test_getCurrentHook() public { - assertEq(address(Lockers.getCurrentHook()), address(0)); - } - - function test_setCurrentHook() public { - Lockers.setCurrentHook(IHooks(address(1))); - assertEq(address(Lockers.getCurrentHook()), address(1)); - } - - function test_setCurrentHook_TwiceDoesNotSucceed() public { - (bool set) = Lockers.setCurrentHook(IHooks(address(1))); - assertTrue(set); - set = Lockers.setCurrentHook(IHooks(address(2))); - assertFalse(set); - assertEq(address(Lockers.getCurrentHook()), address(1)); - } - - function test_clearCurrentHook() public { - Lockers.setCurrentHook(IHooks(address(1))); - assertEq(address(Lockers.getCurrentHook()), address(1)); - Lockers.clearCurrentHook(); - assertEq(address(Lockers.getCurrentHook()), address(0)); - } - - function test_setCurrentHook_afterLock() public { - Lockers.push(address(this), address(this)); - Lockers.setCurrentHook(IHooks(address(1))); - assertEq(address(Lockers.getCurrentHook()), address(1)); - } - - function test_setCurrentHook_beforeAndAfterLock() public { - Lockers.push(address(this), address(this)); - Lockers.setCurrentHook(IHooks(address(2))); - assertEq(address(Lockers.getCurrentHook()), address(2)); - Lockers.push(address(1), address(1)); - assertEq(address(Lockers.getCurrentHook()), address(0)); - } -} From cd92291f2a70160f17b9ca579c8c034d06ca08b0 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Mon, 11 Dec 2023 14:57:53 -0300 Subject: [PATCH 03/19] Remove nested locking --- ...swap hook, already cached dynamic fee.snap | 2 +- .../cached dynamic fee, no hooks.snap | 2 +- .forge-snapshots/donate gas with 1 token.snap | 2 +- .../donate gas with 2 tokens.snap | 2 +- .../gas overhead of no-op lock.snap | 2 +- .forge-snapshots/initialize.snap | 2 +- .forge-snapshots/mint with empty hook.snap | 2 +- .forge-snapshots/mint with native token.snap | 2 +- .forge-snapshots/mint.snap | 2 +- .../mintWithEmptyHookEOAInitiated.snap | 2 +- .../modify position with noop.snap | 2 +- .../poolManager bytecode size.snap | 2 +- .forge-snapshots/simple swap with native.snap | 2 +- .forge-snapshots/simple swap.snap | 2 +- .forge-snapshots/simpleSwapEOAInitiated.snap | 2 +- .../simpleSwapNativeEOAInitiated.snap | 2 +- ...p against liquidity with native token.snap | 2 +- .forge-snapshots/swap against liquidity.snap | 2 +- .../swap burn claim for input.snap | 2 +- .../swap mint output as claim.snap | 2 +- .forge-snapshots/swap with dynamic fee.snap | 2 +- .forge-snapshots/swap with hooks.snap | 2 +- .forge-snapshots/swap with noop.snap | 2 +- .../update dynamic fee in before swap.snap | 2 +- src/PoolManager.sol | 59 +++--- src/interfaces/IPoolManager.sol | 10 +- src/libraries/Hooks.sol | 2 +- src/libraries/Locker.sol | 81 +++++++++ src/libraries/Lockers.sol | 130 ------------- test/LockersLibrary.t.sol | 104 ++--------- test/PoolManagerReentrancyTest.t.sol | 171 ------------------ 31 files changed, 149 insertions(+), 456 deletions(-) create mode 100644 src/libraries/Locker.sol delete mode 100644 src/libraries/Lockers.sol delete mode 100644 test/PoolManagerReentrancyTest.t.sol diff --git a/.forge-snapshots/before swap hook, already cached dynamic fee.snap b/.forge-snapshots/before swap hook, already cached dynamic fee.snap index a51e4dc7d..49651fee8 100644 --- a/.forge-snapshots/before swap hook, already cached dynamic fee.snap +++ b/.forge-snapshots/before swap hook, already cached dynamic fee.snap @@ -1 +1 @@ -190172 \ No newline at end of file +186339 \ No newline at end of file diff --git a/.forge-snapshots/cached dynamic fee, no hooks.snap b/.forge-snapshots/cached dynamic fee, no hooks.snap index 116e65dbf..0ca1c73f5 100644 --- a/.forge-snapshots/cached dynamic fee, no hooks.snap +++ b/.forge-snapshots/cached dynamic fee, no hooks.snap @@ -1 +1 @@ -144157 \ No newline at end of file +140324 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 1 token.snap b/.forge-snapshots/donate gas with 1 token.snap index 1a6d6c7ee..73790f19f 100644 --- a/.forge-snapshots/donate gas with 1 token.snap +++ b/.forge-snapshots/donate gas with 1 token.snap @@ -1 +1 @@ -135379 \ No newline at end of file +133211 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 2 tokens.snap b/.forge-snapshots/donate gas with 2 tokens.snap index 2a457206c..6c5a6b118 100644 --- a/.forge-snapshots/donate gas with 2 tokens.snap +++ b/.forge-snapshots/donate gas with 2 tokens.snap @@ -1 +1 @@ -182276 \ No newline at end of file +178421 \ No newline at end of file diff --git a/.forge-snapshots/gas overhead of no-op lock.snap b/.forge-snapshots/gas overhead of no-op lock.snap index 1a78998d5..1d3443815 100644 --- a/.forge-snapshots/gas overhead of no-op lock.snap +++ b/.forge-snapshots/gas overhead of no-op lock.snap @@ -1 +1 @@ -15246 \ No newline at end of file +15120 \ No newline at end of file diff --git a/.forge-snapshots/initialize.snap b/.forge-snapshots/initialize.snap index 4da269400..3acc92fa5 100644 --- a/.forge-snapshots/initialize.snap +++ b/.forge-snapshots/initialize.snap @@ -1 +1 @@ -73620 \ No newline at end of file +73162 \ No newline at end of file diff --git a/.forge-snapshots/mint with empty hook.snap b/.forge-snapshots/mint with empty hook.snap index 49fa9cf5c..5fa2837ed 100644 --- a/.forge-snapshots/mint with empty hook.snap +++ b/.forge-snapshots/mint with empty hook.snap @@ -1 +1 @@ -316830 \ No newline at end of file +312975 \ No newline at end of file diff --git a/.forge-snapshots/mint with native token.snap b/.forge-snapshots/mint with native token.snap index ee8d8fc5e..655b85cd6 100644 --- a/.forge-snapshots/mint with native token.snap +++ b/.forge-snapshots/mint with native token.snap @@ -1 +1 @@ -196948 \ No newline at end of file +193093 \ No newline at end of file diff --git a/.forge-snapshots/mint.snap b/.forge-snapshots/mint.snap index b57aba5d1..95b04a94a 100644 --- a/.forge-snapshots/mint.snap +++ b/.forge-snapshots/mint.snap @@ -1 +1 @@ -196890 \ No newline at end of file +193035 \ No newline at end of file diff --git a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap index e66edc99e..efb2e0bff 100644 --- a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap +++ b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap @@ -1 +1 @@ -249251 \ No newline at end of file +247083 \ No newline at end of file diff --git a/.forge-snapshots/modify position with noop.snap b/.forge-snapshots/modify position with noop.snap index 7fbc53aff..7c1d70534 100644 --- a/.forge-snapshots/modify position with noop.snap +++ b/.forge-snapshots/modify position with noop.snap @@ -1 +1 @@ -54459 \ No newline at end of file +53978 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 2bb5ec788..4efec4b44 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -23211 \ No newline at end of file +23132 \ No newline at end of file diff --git a/.forge-snapshots/simple swap with native.snap b/.forge-snapshots/simple swap with native.snap index 53c9731f5..90b62aa48 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -192699 \ No newline at end of file +188866 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index 324b12693..3a15f03d9 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -201267 \ No newline at end of file +197434 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapEOAInitiated.snap b/.forge-snapshots/simpleSwapEOAInitiated.snap index 1cd5f151e..f40b6a487 100644 --- a/.forge-snapshots/simpleSwapEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapEOAInitiated.snap @@ -1 +1 @@ -172533 \ No newline at end of file +168722 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap index 4787f4032..d3e4b0504 100644 --- a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap @@ -1 +1 @@ -169195 \ No newline at end of file +165384 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity with native token.snap b/.forge-snapshots/swap against liquidity with native token.snap index a7f63ed6b..647f29ebc 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -123327 \ No newline at end of file +119494 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index 99e49a2b9..ff97d120f 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -110818 \ No newline at end of file +106985 \ No newline at end of file diff --git a/.forge-snapshots/swap burn claim for input.snap b/.forge-snapshots/swap burn claim for input.snap index f34da438c..35c806bf3 100644 --- a/.forge-snapshots/swap burn claim for input.snap +++ b/.forge-snapshots/swap burn claim for input.snap @@ -1 +1 @@ -129969 \ No newline at end of file +126158 \ No newline at end of file diff --git a/.forge-snapshots/swap mint output as claim.snap b/.forge-snapshots/swap mint output as claim.snap index e58b14bf3..3b23424b0 100644 --- a/.forge-snapshots/swap mint output as claim.snap +++ b/.forge-snapshots/swap mint output as claim.snap @@ -1 +1 @@ -213460 \ No newline at end of file +209649 \ No newline at end of file diff --git a/.forge-snapshots/swap with dynamic fee.snap b/.forge-snapshots/swap with dynamic fee.snap index 4fb6d0ab1..a0decf59e 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -189273 \ No newline at end of file +185440 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index 716127b41..4ebfef2b9 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -110796 \ No newline at end of file +106963 \ No newline at end of file diff --git a/.forge-snapshots/swap with noop.snap b/.forge-snapshots/swap with noop.snap index c9c77bffb..1053b45e4 100644 --- a/.forge-snapshots/swap with noop.snap +++ b/.forge-snapshots/swap with noop.snap @@ -1 +1 @@ -47842 \ No newline at end of file +47361 \ No newline at end of file diff --git a/.forge-snapshots/update dynamic fee in before swap.snap b/.forge-snapshots/update dynamic fee in before swap.snap index 9c08b20d9..cb5df9a3e 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -195993 \ No newline at end of file +192160 \ No newline at end of file diff --git a/src/PoolManager.sol b/src/PoolManager.sol index 3ed315e3d..374fda1ec 100644 --- a/src/PoolManager.sol +++ b/src/PoolManager.sol @@ -19,7 +19,7 @@ import {Fees} from "./Fees.sol"; import {Claims} from "./Claims.sol"; import {PoolId, PoolIdLibrary} from "./types/PoolId.sol"; import {BalanceDelta, BalanceDeltaLibrary} from "./types/BalanceDelta.sol"; -import {Lockers} from "./libraries/Lockers.sol"; +import {Locker} from "./libraries/Locker.sol"; import {PoolGetters} from "./libraries/PoolGetters.sol"; /// @notice Holds the state for all pools @@ -88,26 +88,21 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, Claims { } /// @inheritdoc IPoolManager - function getLock(uint256 i) external view override returns (address locker, address lockCaller) { - return (Lockers.getLocker(i), Lockers.getLockCaller(i)); + function getLock() external view override returns (address locker, address lockCaller) { + return (Locker.getLocker(), Locker.getLockCaller()); } /// @notice This will revert if a function is called by any address other than the current locker OR the most recently called, pre-permissioned hook. - modifier onlyByLocker() { - _checkLocker(msg.sender, Lockers.getCurrentLocker()); + modifier isLocked() { + if (!Locker.isLocked()) revert ManagerNotLocked(); _; } - function _checkLocker(address caller, address locker) internal pure { - if (caller == locker) return; - revert LockedBy(locker); - } - /// @inheritdoc IPoolManager function initialize(PoolKey memory key, uint160 sqrtPriceX96, bytes calldata hookData) external override - onlyByLocker + isLocked returns (int24 tick) { if (key.fee.isStaticFeeTooLarge()) revert FeeTooLarge(); @@ -134,35 +129,33 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, Claims { /// @inheritdoc IPoolManager function lock(address lockTarget, bytes calldata data) external payable override returns (bytes memory result) { - Lockers.push(lockTarget, msg.sender); + // Get the lock caller because thats an EOA and is not user-controlable + if (Locker.isLocked()) revert LockedBy(Locker.getLocker()); + + Locker.setLockerAndCaller(lockTarget, msg.sender); // the caller does everything in this callback, including paying what they owe via calls to settle result = ILockCallback(lockTarget).lockAcquired(msg.sender, data); - if (Lockers.length() == 1) { - if (Lockers.nonzeroDeltaCount() != 0) revert CurrencyNotSettled(); - Lockers.clear(); - } else { - Lockers.pop(); - } + if (Locker.nonzeroDeltaCount() != 0) revert CurrencyNotSettled(); + Locker.clearLockerAndCaller(); } function _accountDelta(Currency currency, int128 delta) internal { if (delta == 0) return; - address locker = Lockers.getCurrentLocker(); - int256 current = currencyDelta[locker][currency]; + int256 current = currencyDelta[msg.sender][currency]; int256 next = current + delta; unchecked { if (next == 0) { - Lockers.decrementNonzeroDeltaCount(); + Locker.decrementNonzeroDeltaCount(); } else if (current == 0) { - Lockers.incrementNonzeroDeltaCount(); + Locker.incrementNonzeroDeltaCount(); } } - currencyDelta[locker][currency] = next; + currencyDelta[msg.sender][currency] = next; } /// @dev Accumulates a balance change to a map of currency to balance changes @@ -180,7 +173,7 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, Claims { PoolKey memory key, IPoolManager.ModifyPositionParams memory params, bytes calldata hookData - ) external override noDelegateCall onlyByLocker returns (BalanceDelta delta) { + ) external override noDelegateCall isLocked returns (BalanceDelta delta) { PoolId id = key.toId(); _checkPoolInitialized(id); @@ -210,7 +203,7 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, Claims { external override noDelegateCall - onlyByLocker + isLocked returns (BalanceDelta delta) { PoolId id = key.toId(); @@ -253,7 +246,7 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, Claims { external override noDelegateCall - onlyByLocker + isLocked returns (BalanceDelta delta) { PoolId id = key.toId(); @@ -271,14 +264,14 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, Claims { } /// @inheritdoc IPoolManager - function take(Currency currency, address to, uint256 amount) external override noDelegateCall onlyByLocker { + function take(Currency currency, address to, uint256 amount) external override noDelegateCall isLocked { _accountDelta(currency, amount.toInt128()); reservesOf[currency] -= amount; currency.transfer(to, amount); } /// @inheritdoc IPoolManager - function settle(Currency currency) external payable override noDelegateCall onlyByLocker returns (uint256 paid) { + function settle(Currency currency) external payable override noDelegateCall isLocked returns (uint256 paid) { uint256 reservesBefore = reservesOf[currency]; reservesOf[currency] = currency.balanceOfSelf(); paid = reservesOf[currency] - reservesBefore; @@ -287,13 +280,13 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, Claims { } /// @inheritdoc IPoolManager - function mint(Currency currency, address to, uint256 amount) external noDelegateCall onlyByLocker { + function mint(Currency currency, address to, uint256 amount) external noDelegateCall isLocked { _accountDelta(currency, amount.toInt128()); _mint(to, currency, amount); } /// @inheritdoc IPoolManager - function burn(Currency currency, uint256 amount) external noDelegateCall onlyByLocker { + function burn(Currency currency, uint256 amount) external noDelegateCall isLocked { _accountDelta(currency, -(amount.toInt128())); _burn(currency, amount); } @@ -337,12 +330,8 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, Claims { return value; } - function getLockLength() external view returns (uint256 _length) { - return Lockers.length(); - } - function getLockNonzeroDeltaCount() external view returns (uint256 _nonzeroDeltaCount) { - return Lockers.nonzeroDeltaCount(); + return Locker.nonzeroDeltaCount(); } function getPoolTickInfo(PoolId id, int24 tick) external view returns (Pool.TickInfo memory) { diff --git a/src/interfaces/IPoolManager.sol b/src/interfaces/IPoolManager.sol index 82142f7fe..f9121676f 100644 --- a/src/interfaces/IPoolManager.sol +++ b/src/interfaces/IPoolManager.sol @@ -25,6 +25,9 @@ interface IPoolManager is IFees, IClaims { /// @param locker The current locker error LockedBy(address locker); + /// @notice Thrown when a function is called by an address that is not the current locker + error ManagerNotLocked(); + /// @notice The ERC1155 being deposited is not the Uniswap ERC1155 error NotPoolManagerToken(); @@ -112,11 +115,8 @@ interface IPoolManager is IFees, IClaims { /// @notice Returns the reserves for a given ERC20 currency function reservesOf(Currency currency) external view returns (uint256); - /// @notice Returns the locker in the ith position of the locker queue. - function getLock(uint256 i) external view returns (address locker, address lockCaller); - - /// @notice Returns the length of the lockers array, which is the number of locks open on the PoolManager. - function getLockLength() external view returns (uint256 _length); + /// @notice Returns the locker and lockCaller of the pool + function getLock() external view returns (address locker, address lockCaller); /// @notice Returns the number of nonzero deltas open on the PoolManager that must be zerod by the close of the initial lock. function getLockNonzeroDeltaCount() external view returns (uint256 _nonzeroDeltaCount); diff --git a/src/libraries/Hooks.sol b/src/libraries/Hooks.sol index 361bbbb67..e0c7b9f6a 100644 --- a/src/libraries/Hooks.sol +++ b/src/libraries/Hooks.sol @@ -6,7 +6,7 @@ import {IHooks} from "../interfaces/IHooks.sol"; import {FeeLibrary} from "./FeeLibrary.sol"; import {BalanceDelta} from "../types/BalanceDelta.sol"; import {IPoolManager} from "../interfaces/IPoolManager.sol"; -import {Lockers} from "./Lockers.sol"; +import {Locker} from "./Locker.sol"; /// @notice V4 decides whether to invoke specific hooks by inspecting the leading bits of the address that /// the hooks contract is deployed to. diff --git a/src/libraries/Locker.sol b/src/libraries/Locker.sol new file mode 100644 index 000000000..e1320f933 --- /dev/null +++ b/src/libraries/Locker.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import {IHooks} from "../interfaces/IHooks.sol"; + +/// @notice This is a temporary library that allows us to use transient storage (tstore/tload) +/// for the lockers array and nonzero delta count. +/// TODO: This library can be deleted when we have the transient keyword support in solidity. +library Locker { + // The slot holding the locker, transiently, and the lock caller in the next slot + uint256 constant LOCKER_SLOT = uint256(keccak256("Locker")) - 2; + uint256 constant LOCK_CALLER_SLOT = LOCKER_SLOT + 1; + + // The slot holding the number of nonzero deltas. + uint256 constant NONZERO_DELTA_COUNT = uint256(keccak256("NonzeroDeltaCount")) - 1; + + function setLockerAndCaller(address locker, address lockCaller) internal { + uint256 slot = LOCKER_SLOT; + + assembly { + // set the locker + tstore(slot, locker) + + // set the lock caller + tstore(add(slot, 1), lockCaller) + } + } + + function clearLockerAndCaller() internal { + uint256 slot = LOCKER_SLOT; + assembly { + tstore(slot, 0) + tstore(add(slot, 1), 0) + } + } + + function getLocker() internal view returns (address locker) { + uint256 slot = LOCKER_SLOT; + assembly { + locker := tload(slot) + } + } + + function isLocked() internal view returns (bool) { + return Locker.getLockCaller() != address(0); + } + + function getLockCaller() internal view returns (address locker) { + uint256 slot = LOCK_CALLER_SLOT; + assembly { + locker := tload(slot) + } + } + + function nonzeroDeltaCount() internal view returns (uint256 count) { + uint256 slot = NONZERO_DELTA_COUNT; + assembly { + count := tload(slot) + } + } + + function incrementNonzeroDeltaCount() internal { + uint256 slot = NONZERO_DELTA_COUNT; + assembly { + let count := tload(slot) + count := add(count, 1) + tstore(slot, count) + } + } + + /// @notice Potential to underflow. + /// Current usage ensures this will not happen because we call decrememnt with known boundaries (only up to the numer of times we call increment). + function decrementNonzeroDeltaCount() internal { + uint256 slot = NONZERO_DELTA_COUNT; + assembly { + let count := tload(slot) + count := sub(count, 1) + tstore(slot, count) + } + } +} diff --git a/src/libraries/Lockers.sol b/src/libraries/Lockers.sol deleted file mode 100644 index 43e60842c..000000000 --- a/src/libraries/Lockers.sol +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.20; - -import {IHooks} from "../interfaces/IHooks.sol"; - -/// @notice This is a temporary library that allows us to use transient storage (tstore/tload) -/// for the lockers array and nonzero delta count. -/// TODO: This library can be deleted when we have the transient keyword support in solidity. -library Lockers { - // The starting slot for an array of lockers, stored transiently. - uint256 constant LOCKERS_SLOT = uint256(keccak256("Lockers")) - 1; - - // The number of slots per item in the lockers array - uint256 constant LOCKER_STRUCT_SIZE = 2; - - // The starting slot for an array of hook addresses per locker, stored transiently. - uint256 constant HOOK_ADDRESS_SLOT = uint256(keccak256("HookAddress")) - 1; - - // The slot holding the number of nonzero deltas. - uint256 constant NONZERO_DELTA_COUNT = uint256(keccak256("NonzeroDeltaCount")) - 1; - - // pushes an address tuple (address locker, address lockCaller) - // to the locker array, so each length of the array represents 2 slots of tstorage - function push(address locker, address lockCaller) internal { - uint256 slot = LOCKERS_SLOT; - - uint256 newLength; - uint256 thisLockerSlot; - - unchecked { - newLength = length() + 1; - thisLockerSlot = LOCKERS_SLOT + (newLength * LOCKER_STRUCT_SIZE); - } - - assembly { - // add the locker - tstore(thisLockerSlot, locker) - - // add the lock caller - tstore(add(thisLockerSlot, 1), lockCaller) - - // increase the length - tstore(slot, newLength) - } - } - - function pop() internal { - // decrease the length - uint256 slot = LOCKERS_SLOT; - uint256 newLength; - unchecked { - newLength = length() - 1; - } - assembly { - tstore(slot, newLength) - } - } - - function length() internal view returns (uint256 _length) { - uint256 slot = LOCKERS_SLOT; - assembly { - _length := tload(slot) - } - } - - function clear() internal { - uint256 slot = LOCKERS_SLOT; - assembly { - tstore(slot, 0) - } - } - - function getLocker(uint256 i) internal view returns (address locker) { - // first slot of the ith array item - uint256 slot = LOCKERS_SLOT + (i * LOCKER_STRUCT_SIZE); - assembly { - locker := tload(slot) - } - } - - function getLockCaller(uint256 i) internal view returns (address locker) { - // second slot of the ith array item - uint256 slot = LOCKERS_SLOT + (i * LOCKER_STRUCT_SIZE + 1); - assembly { - locker := tload(slot) - } - } - - function getCurrentLocker() internal view returns (address locker) { - return getLocker(length()); - } - - function getCurrentLockCaller() internal view returns (address locker) { - return getLockCaller(length()); - } - - function nonzeroDeltaCount() internal view returns (uint256 count) { - uint256 slot = NONZERO_DELTA_COUNT; - assembly { - count := tload(slot) - } - } - - function incrementNonzeroDeltaCount() internal { - uint256 slot = NONZERO_DELTA_COUNT; - assembly { - let count := tload(slot) - count := add(count, 1) - tstore(slot, count) - } - } - - /// @notice Potential to underflow. - /// Current usage ensures this will not happen because we call decrememnt with known boundaries (only up to the numer of times we call increment). - function decrementNonzeroDeltaCount() internal { - uint256 slot = NONZERO_DELTA_COUNT; - assembly { - let count := tload(slot) - count := sub(count, 1) - tstore(slot, count) - } - } - - function getHook(uint256 i) internal view returns (address hook) { - uint256 slot = HOOK_ADDRESS_SLOT + i; - assembly { - hook := tload(slot) - } - } -} diff --git a/test/LockersLibrary.t.sol b/test/LockersLibrary.t.sol index a2d73cf09..00791bb60 100644 --- a/test/LockersLibrary.t.sol +++ b/test/LockersLibrary.t.sol @@ -10,7 +10,7 @@ import {Currency, CurrencyLibrary} from "../src/types/Currency.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {PoolKey} from "../src/types/PoolKey.sol"; import {IHooks} from "../src/interfaces/IHooks.sol"; -import {Lockers} from "../src/libraries/Lockers.sol"; +import {Locker} from "../src/libraries/Locker.sol"; contract LockersLibraryTest is Test, Deployers, ILockCallback { using CurrencyLibrary for Currency; @@ -22,126 +22,50 @@ contract LockersLibraryTest is Test, Deployers, ILockCallback { } function testLockerLengthAndNonzeroDeltaCount() public { - (uint256 lengthDuringLockCallback, uint256 nonzeroDeltaCountDuringCallback) = - abi.decode(manager.lock(address(this), ""), (uint256, uint256)); - assertEq(lengthDuringLockCallback, 1); + (uint256 nonzeroDeltaCountDuringCallback) = abi.decode(manager.lock(address(this), ""), (uint256)); assertEq(nonzeroDeltaCountDuringCallback, 1); - assertEq(manager.getLockLength(), 0); assertEq(manager.getLockNonzeroDeltaCount(), 0); } function lockAcquired(address, bytes calldata) public returns (bytes memory) { - uint256 len = manager.getLockLength(); - // apply a delta and save count manager.take(key.currency0, address(this), 1); uint256 count = manager.getLockNonzeroDeltaCount(); key.currency0.transfer(address(manager), 1); manager.settle(key.currency0); - bytes memory data = abi.encode(len, count); + bytes memory data = abi.encode(count); return data; } - function test_push() public { - Lockers.push(address(this), address(1)); - assertEq(Lockers.length(), 1); - assertEq(Lockers.getLocker(LOCKERS_OFFSET), address(this)); - assertEq(Lockers.getLockCaller(LOCKERS_OFFSET), address(1)); - } - - function test_push_multipleAddressesFuzz(address[2][] memory addrs) public { - for (uint256 i = 0; i < addrs.length; i++) { - address[2] memory loopAddrs = addrs[i]; - address locker = loopAddrs[0]; - address lockCaller = loopAddrs[1]; - assertEq(Lockers.length(), i); - Lockers.push(locker, lockCaller); - assertEq(Lockers.length(), LOCKERS_OFFSET + i); - assertEq(Lockers.getLocker(LOCKERS_OFFSET + i), locker); - assertEq(Lockers.getLockCaller(LOCKERS_OFFSET + i), lockCaller); - } - } - - function test_getCurrentLocker_multipleAddressesFuzz(address[2][] memory addrs) public { - for (uint256 i = 0; i < addrs.length; i++) { - address[2] memory loopAddrs = addrs[i]; - address locker = loopAddrs[0]; - address lockCaller = loopAddrs[1]; - assertEq(Lockers.length(), i); - Lockers.push(locker, lockCaller); - assertEq(Lockers.length(), LOCKERS_OFFSET + i); - assertEq(Lockers.getCurrentLocker(), locker); - assertEq(Lockers.getCurrentLockCaller(), lockCaller); - } - } - - function test_pop() public { - Lockers.push(address(this), address(1)); - assertEq(Lockers.length(), 1); - Lockers.pop(); - assertEq(Lockers.length(), 0); - } - - function test_pop_multipleAddressesFuzz(address[2][] memory addrs) public { - for (uint256 i = 0; i < addrs.length; i++) { - address[2] memory loopAddrs = addrs[i]; - address locker = loopAddrs[0]; - address lockCaller = loopAddrs[1]; - Lockers.push(locker, lockCaller); - } - - assertEq(Lockers.length(), addrs.length); - - for (uint256 i = 0; i < addrs.length; i++) { - Lockers.pop(); - assertEq(Lockers.length(), addrs.length - i - LOCKERS_OFFSET); - } - - assertEq(Lockers.length(), 0); - } - - function test_clear(address[2][] memory addrs) public { - for (uint256 i = 0; i < addrs.length; i++) { - address[2] memory loopAddrs = addrs[i]; - address locker = loopAddrs[0]; - address lockCaller = loopAddrs[1]; - Lockers.push(locker, lockCaller); - } - - assertEq(Lockers.length(), addrs.length); - Lockers.clear(); - assertEq(Lockers.length(), 0); - } - function test_incrementNonzeroDeltaCount() public { - Lockers.incrementNonzeroDeltaCount(); - assertEq(Lockers.nonzeroDeltaCount(), 1); + Locker.incrementNonzeroDeltaCount(); + assertEq(Locker.nonzeroDeltaCount(), 1); } function test_incrementNonzeroDeltaCountFuzz(uint8 count) public { for (uint256 i = 0; i < count; i++) { - Lockers.incrementNonzeroDeltaCount(); - assertEq(Lockers.nonzeroDeltaCount(), i + 1); + Locker.incrementNonzeroDeltaCount(); + assertEq(Locker.nonzeroDeltaCount(), i + 1); } } function test_decrementNonzeroDeltaCount() public { - Lockers.incrementNonzeroDeltaCount(); - Lockers.decrementNonzeroDeltaCount(); - assertEq(Lockers.nonzeroDeltaCount(), 0); + Locker.incrementNonzeroDeltaCount(); + Locker.decrementNonzeroDeltaCount(); + assertEq(Locker.nonzeroDeltaCount(), 0); } function test_decrementNonzeroDeltaCountFuzz(uint8 count) public { for (uint256 i = 0; i < count; i++) { - Lockers.incrementNonzeroDeltaCount(); + Locker.incrementNonzeroDeltaCount(); } - assertEq(Lockers.nonzeroDeltaCount(), count); + assertEq(Locker.nonzeroDeltaCount(), count); for (uint256 i = 0; i < count; i++) { - Lockers.decrementNonzeroDeltaCount(); - assertEq(Lockers.nonzeroDeltaCount(), count - i - 1); + Locker.decrementNonzeroDeltaCount(); + assertEq(Locker.nonzeroDeltaCount(), count - i - 1); } } } diff --git a/test/PoolManagerReentrancyTest.t.sol b/test/PoolManagerReentrancyTest.t.sol deleted file mode 100644 index 8eb25d4c6..000000000 --- a/test/PoolManagerReentrancyTest.t.sol +++ /dev/null @@ -1,171 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; - -import {Test} from "forge-std/Test.sol"; -import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {Currency, CurrencyLibrary} from "../src/types/Currency.sol"; -import {IPoolManager} from "../src/interfaces/IPoolManager.sol"; -import {IHooks} from "../src/interfaces/IHooks.sol"; -import {ILockCallback} from "../src/interfaces/callback/ILockCallback.sol"; -import {PoolManager} from "../src/PoolManager.sol"; -import {Deployers} from "./utils/Deployers.sol"; - -contract TokenLocker is ILockCallback { - using CurrencyLibrary for Currency; - - function main(IPoolManager manager, Currency currency, bool reclaim) external { - manager.lock(address(this), abi.encode(currency, reclaim)); - } - - function lockAcquired(address, bytes calldata data) external returns (bytes memory) { - (Currency currency, bool reclaim) = abi.decode(data, (Currency, bool)); - - IPoolManager manager = IPoolManager(msg.sender); - uint256 count = manager.getLockNonzeroDeltaCount(); - assert(count == 0); - - int256 delta = manager.currencyDelta(address(this), currency); - assert(delta == 0); - - // deposit some tokens - currency.transfer(address(manager), 1); - manager.settle(currency); - count = manager.getLockNonzeroDeltaCount(); - assert(count == 1); - delta = manager.currencyDelta(address(this), currency); - assert(delta == -1); - - // take them back - if (reclaim) { - manager.take(currency, address(this), 1); - count = manager.getLockNonzeroDeltaCount(); - assert(count == 0); - delta = manager.currencyDelta(address(this), currency); - assert(delta == 0); - } - - return ""; - } -} - -contract SimpleLinearLocker is ILockCallback { - function(uint256) external checker; - - function main(IPoolManager manager, uint256 timesToReenter, function(uint256) external checker_) external { - checker = checker_; - manager.lock(address(this), abi.encode(timesToReenter, 0)); - } - - function lockAcquired(address, bytes calldata data) external returns (bytes memory) { - (uint256 timesToReenter, uint256 depth) = abi.decode(data, (uint256, uint256)); - checker(depth); - if (depth < timesToReenter) { - IPoolManager manager = IPoolManager(msg.sender); - manager.lock(address(this), abi.encode(timesToReenter, depth + 1)); - } - return ""; - } -} - -contract ParallelLocker is ILockCallback { - // We define an INDEX_OFFSET at 1 since the first locker is placed at index 1. - // The 0th index is used for storing the length. - uint256 constant INDEX_OFFSET = 1; - IPoolManager manager; - - constructor(IPoolManager manager_) { - manager = manager_; - } - - function main() external { - manager.lock(address(this), ""); - } - - function assertionChecker0(uint256) external view { - uint256 length = manager.getLockLength(); - assert(length == 2); - (address locker,) = manager.getLock(INDEX_OFFSET + 1); - assert(locker == msg.sender); - } - - function assertionChecker1(uint256 depth) external view { - uint256 length = manager.getLockLength(); - assert(length == depth + 2); - (address locker,) = manager.getLock(INDEX_OFFSET + depth + 1); - assert(locker == msg.sender); - } - - function assertionChecker2(uint256) external view { - uint256 length = manager.getLockLength(); - assert(length == 2); - (address locker,) = manager.getLock(INDEX_OFFSET + 1); - assert(locker == msg.sender); - } - - function lockAcquired(address, bytes calldata) external returns (bytes memory) { - SimpleLinearLocker locker0 = new SimpleLinearLocker(); - SimpleLinearLocker locker1 = new SimpleLinearLocker(); - SimpleLinearLocker locker2 = new SimpleLinearLocker(); - - uint256 length = manager.getLockLength(); - assert(length == 1); - (address locker,) = manager.getLock(INDEX_OFFSET + 0); - assert(locker == address(this)); - - locker0.main(manager, 0, this.assertionChecker0); - uint256 length1 = manager.getLockLength(); - assert(length1 == 1); - - locker1.main(manager, 1, this.assertionChecker1); - uint256 length2 = manager.getLockLength(); - assert(length2 == 1); - - locker2.main(manager, 0, this.assertionChecker2); - uint256 length3 = manager.getLockLength(); - assert(length3 == 1); - - return ""; - } -} - -contract PoolManagerReentrancyTest is Test, Deployers { - uint256 constant INDEX_OFFSET = 1; - - function setUp() public { - initializeManagerRoutersAndPoolsWithLiq(IHooks(address(0))); - } - - function testTokenLocker() public { - TokenLocker locker = new TokenLocker(); - MockERC20(Currency.unwrap(currency0)).mint(address(locker), 1); - MockERC20(Currency.unwrap(currency0)).approve(address(locker), 1); - locker.main(manager, currency0, true); - } - - function testTokenRevert() public { - TokenLocker locker = new TokenLocker(); - MockERC20(Currency.unwrap(currency0)).mint(address(locker), 1); - MockERC20(Currency.unwrap(currency0)).approve(address(locker), 1); - vm.expectRevert(abi.encodeWithSelector(IPoolManager.CurrencyNotSettled.selector)); - locker.main(manager, currency0, false); - } - - function assertionChecker(uint256 depth) external { - uint256 length = manager.getLockLength(); - assertEq(length, depth + 1); - (address locker,) = manager.getLock(INDEX_OFFSET + depth); - assertEq(locker, msg.sender); - } - - function testSimpleLinearLocker() public { - SimpleLinearLocker locker = new SimpleLinearLocker(); - locker.main(manager, 0, this.assertionChecker); - locker.main(manager, 1, this.assertionChecker); - locker.main(manager, 2, this.assertionChecker); - } - - function testParallelLocker() public { - ParallelLocker locker = new ParallelLocker(manager); - locker.main(); - } -} From 1f4961e127266dfc728af9412d0a798f090fff5f Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Tue, 12 Dec 2023 14:09:22 -0300 Subject: [PATCH 04/19] Locker library tests --- test/Locker.t.sol | 81 +++++++++++++++++++++++++++++++++++++++ test/LockersLibrary.t.sol | 71 ---------------------------------- 2 files changed, 81 insertions(+), 71 deletions(-) create mode 100644 test/Locker.t.sol delete mode 100644 test/LockersLibrary.t.sol diff --git a/test/Locker.t.sol b/test/Locker.t.sol new file mode 100644 index 000000000..fc9647cb5 --- /dev/null +++ b/test/Locker.t.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; +import {Locker} from "../src/libraries/Locker.sol"; + +contract CurrentHookAddressTest is Test { + address constant ADDRESS_AS = 0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa; + address constant ADDRESS_BS = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; + + function test_setLockerAndCaller() public { + assertEq(address(Locker.getLocker()), address(0)); + assertEq(address(Locker.getLockCaller()), address(0)); + + Locker.setLockerAndCaller(ADDRESS_AS, ADDRESS_BS); + + assertEq(address(Locker.getLocker()), ADDRESS_AS); + assertEq(address(Locker.getLockCaller()), ADDRESS_BS); + + // in the way this library is used in V4, this function will never be called when non-0 + Locker.setLockerAndCaller(ADDRESS_BS, ADDRESS_AS); + + assertEq(address(Locker.getLocker()), ADDRESS_BS); + assertEq(address(Locker.getLockCaller()), ADDRESS_AS); + } + + function test_clearLockerAndCaller() public { + Locker.setLockerAndCaller(ADDRESS_AS, ADDRESS_BS); + + assertEq(address(Locker.getLocker()), ADDRESS_AS); + assertEq(address(Locker.getLockCaller()), ADDRESS_BS); + + Locker.clearLockerAndCaller(); + + assertEq(address(Locker.getLocker()), address(0)); + assertEq(address(Locker.getLockCaller()), address(0)); + } + + function test_isLocked() public { + assertFalse(Locker.isLocked()); + + Locker.setLockerAndCaller(ADDRESS_AS, ADDRESS_BS); + + assertTrue(Locker.isLocked()); + + Locker.clearLockerAndCaller(); + + assertFalse(Locker.isLocked()); + } + + function test_incrementNonzeroDeltaCount() public { + Locker.incrementNonzeroDeltaCount(); + assertEq(Locker.nonzeroDeltaCount(), 1); + } + + function test_decrementNonzeroDeltaCount() public { + Locker.incrementNonzeroDeltaCount(); + Locker.decrementNonzeroDeltaCount(); + assertEq(Locker.nonzeroDeltaCount(), 0); + } + + // Reading from right to left. Bit of 0: call increase. Bit of 1: call decrease. + // The library allows over over/underflow so we dont check for that here + function test_nonZeroDeltaCount_fuzz(uint256 instructions) public { + uint256 expectedCount; + for (uint256 i = 0; i < 256; i++) { + if ((instructions & (1 << i)) == 0) { + Locker.incrementNonzeroDeltaCount(); + unchecked { + expectedCount++; + } + } else { + Locker.decrementNonzeroDeltaCount(); + unchecked { + expectedCount--; + } + } + assertEq(Locker.nonzeroDeltaCount(), expectedCount); + } + } +} diff --git a/test/LockersLibrary.t.sol b/test/LockersLibrary.t.sol deleted file mode 100644 index 00791bb60..000000000 --- a/test/LockersLibrary.t.sol +++ /dev/null @@ -1,71 +0,0 @@ -pragma solidity ^0.8.20; - -import {Test} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; -import {PoolManager} from "../src/PoolManager.sol"; -import {IPoolManager} from "../src/interfaces/IPoolManager.sol"; -import {Deployers} from "./utils/Deployers.sol"; -import {ILockCallback} from "../src/interfaces/callback/ILockCallback.sol"; -import {Currency, CurrencyLibrary} from "../src/types/Currency.sol"; -import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {PoolKey} from "../src/types/PoolKey.sol"; -import {IHooks} from "../src/interfaces/IHooks.sol"; -import {Locker} from "../src/libraries/Locker.sol"; - -contract LockersLibraryTest is Test, Deployers, ILockCallback { - using CurrencyLibrary for Currency; - - uint256 constant LOCKERS_OFFSET = uint256(1); - - function setUp() public { - initializeManagerRoutersAndPoolsWithLiq(IHooks(address(0))); - } - - function testLockerLengthAndNonzeroDeltaCount() public { - (uint256 nonzeroDeltaCountDuringCallback) = abi.decode(manager.lock(address(this), ""), (uint256)); - assertEq(nonzeroDeltaCountDuringCallback, 1); - assertEq(manager.getLockNonzeroDeltaCount(), 0); - } - - function lockAcquired(address, bytes calldata) public returns (bytes memory) { - // apply a delta and save count - manager.take(key.currency0, address(this), 1); - uint256 count = manager.getLockNonzeroDeltaCount(); - key.currency0.transfer(address(manager), 1); - manager.settle(key.currency0); - - bytes memory data = abi.encode(count); - return data; - } - - function test_incrementNonzeroDeltaCount() public { - Locker.incrementNonzeroDeltaCount(); - assertEq(Locker.nonzeroDeltaCount(), 1); - } - - function test_incrementNonzeroDeltaCountFuzz(uint8 count) public { - for (uint256 i = 0; i < count; i++) { - Locker.incrementNonzeroDeltaCount(); - assertEq(Locker.nonzeroDeltaCount(), i + 1); - } - } - - function test_decrementNonzeroDeltaCount() public { - Locker.incrementNonzeroDeltaCount(); - Locker.decrementNonzeroDeltaCount(); - assertEq(Locker.nonzeroDeltaCount(), 0); - } - - function test_decrementNonzeroDeltaCountFuzz(uint8 count) public { - for (uint256 i = 0; i < count; i++) { - Locker.incrementNonzeroDeltaCount(); - } - - assertEq(Locker.nonzeroDeltaCount(), count); - - for (uint256 i = 0; i < count; i++) { - Locker.decrementNonzeroDeltaCount(); - assertEq(Locker.nonzeroDeltaCount(), count - i - 1); - } - } -} From c0e2722590ba18223249ac0071c6e40399103387 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Tue, 12 Dec 2023 14:34:57 -0300 Subject: [PATCH 05/19] Rename empty lock test --- src/libraries/Locker.sol | 2 +- src/test/{PoolLockTest.sol => PoolEmptyLockTest.sol} | 2 +- test/PoolManager.t.sol | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) rename src/test/{PoolLockTest.sol => PoolEmptyLockTest.sol} (93%) diff --git a/src/libraries/Locker.sol b/src/libraries/Locker.sol index e1320f933..c37ab411c 100644 --- a/src/libraries/Locker.sol +++ b/src/libraries/Locker.sol @@ -69,7 +69,7 @@ library Locker { } /// @notice Potential to underflow. - /// Current usage ensures this will not happen because we call decrememnt with known boundaries (only up to the numer of times we call increment). + /// Current usage ensures this will not happen because we call decrememnt with known boundaries (only up to the number of times we call increment). function decrementNonzeroDeltaCount() internal { uint256 slot = NONZERO_DELTA_COUNT; assembly { diff --git a/src/test/PoolLockTest.sol b/src/test/PoolEmptyLockTest.sol similarity index 93% rename from src/test/PoolLockTest.sol rename to src/test/PoolEmptyLockTest.sol index a312fcf2f..3ff803336 100644 --- a/src/test/PoolLockTest.sol +++ b/src/test/PoolEmptyLockTest.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import {IPoolManager} from "../interfaces/IPoolManager.sol"; import {ILockCallback} from "../interfaces/callback/ILockCallback.sol"; -contract PoolLockTest is ILockCallback { +contract PoolEmptyLockTest is ILockCallback { event LockAcquired(); IPoolManager manager; diff --git a/test/PoolManager.t.sol b/test/PoolManager.t.sol index ba24f930a..96c60d738 100644 --- a/test/PoolManager.t.sol +++ b/test/PoolManager.t.sol @@ -22,7 +22,7 @@ import {BalanceDelta, BalanceDeltaLibrary} from "../src/types/BalanceDelta.sol"; import {PoolSwapTest} from "../src/test/PoolSwapTest.sol"; import {TestInvalidERC20} from "../src/test/TestInvalidERC20.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; -import {PoolLockTest} from "../src/test/PoolLockTest.sol"; +import {PoolEmptyLockTest} from "../src/test/PoolEmptyLockTest.sol"; import {PoolId, PoolIdLibrary} from "../src/types/PoolId.sol"; import {FeeLibrary} from "../src/libraries/FeeLibrary.sol"; import {Position} from "../src/libraries/Position.sol"; @@ -52,7 +52,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { event Burn(address indexed from, Currency indexed currency, uint256 amount); event ProtocolFeeUpdated(PoolId indexed id, uint16 protocolFee); - PoolLockTest lockTest; + PoolEmptyLockTest emptyLockRouter; address ADDRESS_ZERO = address(0); address EMPTY_HOOKS = address(0xf000000000000000000000000000000000000000); @@ -61,7 +61,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function setUp() public { initializeManagerRoutersAndPoolsWithLiq(IHooks(address(0))); - lockTest = new PoolLockTest(manager); + emptyLockRouter = new PoolEmptyLockTest(manager); } function test_bytecodeSize() public { @@ -1115,16 +1115,16 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { assertEq(manager.protocolFeesAccrued(nativeCurrency), 0); } - function test_lock_NoOpIsOk() public { + function test_lock_NoOp_gas() public { snapStart("gas overhead of no-op lock"); - lockTest.lock(); + emptyLockRouter.lock(); snapEnd(); } function test_lock_EmitsCorrectId() public { vm.expectEmit(false, false, false, true); emit LockAcquired(); - lockTest.lock(); + emptyLockRouter.lock(); } // function testExtsloadForPoolPrice() public { From 2237a48c1dfc1459bf0c2be2a579fb4919089427 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Tue, 12 Dec 2023 20:18:25 -0300 Subject: [PATCH 06/19] Test skeleton --- src/interfaces/IPoolManager.sol | 4 +- src/test/PoolNestedActionsTest.sol | 135 +++++++++++++++++++++++++++++ test/NestedActions.t.sol | 14 +++ test/PoolManager.t.sol | 31 ++++--- test/PoolManagerInitialize.t.sol | 14 ++- test/utils/Constants.sol | 6 ++ test/utils/Deployers.sol | 6 +- 7 files changed, 186 insertions(+), 24 deletions(-) create mode 100644 src/test/PoolNestedActionsTest.sol create mode 100644 test/NestedActions.t.sol diff --git a/src/interfaces/IPoolManager.sol b/src/interfaces/IPoolManager.sol index f9121676f..dfdb53107 100644 --- a/src/interfaces/IPoolManager.sol +++ b/src/interfaces/IPoolManager.sol @@ -21,11 +21,11 @@ interface IPoolManager is IFees, IClaims { /// @notice Thrown when trying to interact with a non-initialized pool error PoolNotInitialized(); - /// @notice Thrown when a function is called by an address that is not the current locker + /// @notice Thrown when lock is called, but a lock is already open /// @param locker The current locker error LockedBy(address locker); - /// @notice Thrown when a function is called by an address that is not the current locker + /// @notice Thrown when a function is called outside of a lock error ManagerNotLocked(); /// @notice The ERC1155 being deposited is not the Uniswap ERC1155 diff --git a/src/test/PoolNestedActionsTest.sol b/src/test/PoolNestedActionsTest.sol new file mode 100644 index 000000000..7dab785b0 --- /dev/null +++ b/src/test/PoolNestedActionsTest.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {IPoolManager} from "../interfaces/IPoolManager.sol"; +import {ILockCallback} from "../interfaces/callback/ILockCallback.sol"; +import {PoolTestBase} from "./PoolTestBase.sol"; +import {PoolKey} from "../types/PoolKey.sol"; +import {Constants} from "../../test/utils/Constants.sol"; +import {Test} from "forge-std/Test.sol"; +import {BalanceDelta} from "../types/BalanceDelta.sol"; +import {Currency} from "../types/Currency.sol"; + +enum Action { + NESTED_SELF_LOCK, + NESTED_EXECUTOR_LOCK, + SWAP_AND_SETTLE, + DONATE_AND_SETTLE, + ADD_LIQ_AND_SETTLE, + REMOVE_LIQ_AND_SETTLE +} + +contract PoolNestedActionsTest is Test, ILockCallback { + IPoolManager manager; + NestedActionExecutor public executor; + + constructor(IPoolManager _manager) { + manager = _manager; + executor = new NestedActionExecutor(manager); + } + + function lock(Action[] memory actions) external { + manager.lock(address(this), abi.encode(actions)); + } + + /// @notice Called by the pool manager on `msg.sender` when a lock is acquired + function lockAcquired(address, bytes calldata data) external override returns (bytes memory) { + Action[] memory actions = abi.decode(data, (Action[])); + if (actions.length == 1 && actions[0] == Action.NESTED_SELF_LOCK) { + _nestedLock(); + } else { + executor.execute(actions); + } + return ""; + } + + function _nestedLock() internal { + (address locker, address lockCaller) = manager.getLock(); + assertEq(locker, address(this)); + assertEq(lockCaller, address(this)); + + vm.expectRevert(abi.encodeWithSelector(IPoolManager.LockedBy.selector, address(this))); + manager.lock(address(this), ""); + + (locker, lockCaller) = manager.getLock(); + assertEq(locker, address(this)); + assertEq(lockCaller, address(this)); + } +} + +contract NestedActionExecutor is Test, PoolTestBase { + PoolKey internal key; + + error KeyNotSet(); + + IPoolManager.ModifyPositionParams internal ADD_LIQ_PARAMS = + IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}); + + IPoolManager.ModifyPositionParams internal REMOVE_LIQ_PARAMS = + IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: -1e18}); + + IPoolManager.SwapParams internal SWAP_PARAMS = + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: Constants.SQRT_RATIO_1_2}); + + constructor(IPoolManager _manager) PoolTestBase(_manager) {} + + function setKey(PoolKey memory _key) external { + key = _key; + } + + function execute(Action[] memory actions) public { + if (Currency.unwrap(key.currency0) == address(0)) revert KeyNotSet(); + for (uint256 i = 0; i < actions.length; i++) { + Action action = actions[i]; + if (action == Action.NESTED_EXECUTOR_LOCK) _nestedLock(); + else if (action == Action.SWAP_AND_SETTLE) _swap(); + else if (action == Action.ADD_LIQ_AND_SETTLE) _addLiquidity(); + else if (action == Action.REMOVE_LIQ_AND_SETTLE) _removeLiquidity(); + else if (action == Action.DONATE_AND_SETTLE) _donate(); + } + } + + function _nestedLock() internal { + (address locker, address lockCaller) = manager.getLock(); + assertEq(locker, msg.sender); + assertEq(lockCaller, msg.sender); + + vm.expectRevert(abi.encodeWithSelector(IPoolManager.LockedBy.selector, msg.sender)); + manager.lock(address(this), ""); + + (locker, lockCaller) = manager.getLock(); + assertEq(locker, msg.sender); + assertEq(lockCaller, msg.sender); + } + + function _swap() internal { + // swap without a lock, checking that the deltas are applied to this contract's address + (,,, int256 deltaLockerBefore0) = _fetchBalances(key.currency0, msg.sender); + (,,, int256 deltaLockerBefore1) = _fetchBalances(key.currency1, msg.sender); + (,,, int256 deltaThisBefore0) = _fetchBalances(key.currency0, address(this)); + (,,, int256 deltaThisBefore1) = _fetchBalances(key.currency1, address(this)); + + BalanceDelta delta = manager.swap(key, SWAP_PARAMS, ""); + + (,,, int256 deltaLockerAfter0) = _fetchBalances(key.currency0, msg.sender); + (,,, int256 deltaLockerAfter1) = _fetchBalances(key.currency1, msg.sender); + (,,, int256 deltaThisAfter0) = _fetchBalances(key.currency0, address(this)); + (,,, int256 deltaThisAfter1) = _fetchBalances(key.currency1, address(this)); + + assertEq(deltaLockerBefore0, deltaLockerAfter0); + assertEq(deltaLockerBefore1, deltaLockerAfter1); + assertEq(deltaThisBefore0 + SWAP_PARAMS.amountSpecified, deltaThisAfter0); + assertEq(deltaThisBefore1 - 98, deltaThisAfter1); + assertEq(delta.amount0(), deltaThisAfter0); + assertEq(delta.amount1(), deltaThisAfter1); + } + + function _addLiquidity() internal {} + function _removeLiquidity() internal {} + function _donate() internal {} + + // This will never actually be used - its just to allow us to use the PoolTestBase helper contact + function lockAcquired(address, bytes calldata) external pure override returns (bytes memory) { + return ""; + } +} diff --git a/test/NestedActions.t.sol b/test/NestedActions.t.sol new file mode 100644 index 000000000..411e433bc --- /dev/null +++ b/test/NestedActions.t.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; +import {Deployers} from "./utils/Deployers.sol"; +import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; +import {Action} from "../src/test/PoolNestedActionsTest.sol"; +import {IHooks} from "../src/interfaces/IHooks.sol"; + +contract NestedActions is Test, Deployers, GasSnapshot { + function setUp() public { + initializeManagerRoutersAndPoolsWithLiq(IHooks(address(0))); + } +} diff --git a/test/PoolManager.t.sol b/test/PoolManager.t.sol index 96c60d738..9680112b7 100644 --- a/test/PoolManager.t.sol +++ b/test/PoolManager.t.sol @@ -2,13 +2,10 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; import {IHooks} from "../src/interfaces/IHooks.sol"; import {Hooks} from "../src/libraries/Hooks.sol"; import {IPoolManager} from "../src/interfaces/IPoolManager.sol"; import {IFees} from "../src/interfaces/IFees.sol"; -import {IProtocolFeeController} from "../src/interfaces/IProtocolFeeController.sol"; -import {PoolManager} from "../src/PoolManager.sol"; import {TickMath} from "../src/libraries/TickMath.sol"; import {Pool} from "../src/libraries/Pool.sol"; import {Deployers} from "./utils/Deployers.sol"; @@ -23,9 +20,11 @@ import {PoolSwapTest} from "../src/test/PoolSwapTest.sol"; import {TestInvalidERC20} from "../src/test/TestInvalidERC20.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {PoolEmptyLockTest} from "../src/test/PoolEmptyLockTest.sol"; +import {Action} from "../src/test/PoolNestedActionsTest.sol"; import {PoolId, PoolIdLibrary} from "../src/types/PoolId.sol"; import {FeeLibrary} from "../src/libraries/FeeLibrary.sol"; import {Position} from "../src/libraries/Position.sol"; +import {Constants} from "./utils/Constants.sol"; contract PoolManagerTest is Test, Deployers, GasSnapshot { using Hooks for IHooks; @@ -54,10 +53,6 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { PoolEmptyLockTest emptyLockRouter; - address ADDRESS_ZERO = address(0); - address EMPTY_HOOKS = address(0xf000000000000000000000000000000000000000); - address MOCK_HOOKS = address(0xfF00000000000000000000000000000000000000); - function setUp() public { initializeManagerRoutersAndPoolsWithLiq(IHooks(address(0))); @@ -117,7 +112,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { address payable mockAddr = payable(address(uint160(Hooks.BEFORE_MODIFY_POSITION_FLAG | Hooks.AFTER_MODIFY_POSITION_FLAG))); - address payable hookAddr = payable(MOCK_HOOKS); + address payable hookAddr = payable(Constants.MOCK_HOOKS); vm.etch(hookAddr, vm.getDeployedCode("EmptyTestHooks.sol:EmptyTestHooks")); MockContract mockContract = new MockContract(); @@ -199,7 +194,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { } function test_mint_withHooks_gas() public { - address hookEmptyAddr = EMPTY_HOOKS; + address hookEmptyAddr = Constants.EMPTY_HOOKS; MockHooks impl = new MockHooks(); vm.etch(hookEmptyAddr, address(impl).code); MockHooks mockHooks = MockHooks(hookEmptyAddr); @@ -212,7 +207,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { } function test_mint_withHooks_EOAInitiated() public { - address hookEmptyAddr = EMPTY_HOOKS; + address hookEmptyAddr = Constants.EMPTY_HOOKS; MockHooks impl = new MockHooks(); vm.etch(hookEmptyAddr, address(impl).code); MockHooks mockHooks = MockHooks(hookEmptyAddr); @@ -363,7 +358,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function test_swap_succeedsWithHooksIfInitialized() public { address payable mockAddr = payable(address(uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG))); - address payable hookAddr = payable(MOCK_HOOKS); + address payable hookAddr = payable(Constants.MOCK_HOOKS); vm.etch(hookAddr, vm.getDeployedCode("EmptyTestHooks.sol:EmptyTestHooks")); MockContract mockContract = new MockContract(); @@ -470,7 +465,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { } function test_swap_withHooks_gas() public { - address hookEmptyAddr = EMPTY_HOOKS; + address hookEmptyAddr = Constants.EMPTY_HOOKS; MockHooks impl = new MockHooks(); vm.etch(hookEmptyAddr, address(impl).code); @@ -1127,6 +1122,18 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { emptyLockRouter.lock(); } + Action[] actions; + + function test_lock_cannotBeCalledTwiceByLocker() public { + actions = [Action.NESTED_SELF_LOCK]; + nestedActionRouter.lock(actions); + } + + function test_lock_cannotBeCalledTwiceByDifferentLockers() public { + actions = [Action.NESTED_EXECUTOR_LOCK]; + nestedActionRouter.lock(actions); + } + // function testExtsloadForPoolPrice() public { // IPoolManager.key = IPoolManager.PoolKey({ // currency0: currency0, diff --git a/test/PoolManagerInitialize.t.sol b/test/PoolManagerInitialize.t.sol index 313e439d7..75a8d8544 100644 --- a/test/PoolManagerInitialize.t.sol +++ b/test/PoolManagerInitialize.t.sol @@ -10,6 +10,7 @@ import {PoolManager} from "../src/PoolManager.sol"; import {TickMath} from "../src/libraries/TickMath.sol"; import {Pool} from "../src/libraries/Pool.sol"; import {Deployers} from "./utils/Deployers.sol"; +import {Constants} from "./utils/Constants.sol"; import {Currency, CurrencyLibrary} from "../src/types/Currency.sol"; import {MockHooks} from "../src/test/MockHooks.sol"; import {MockContract} from "../src/test/MockContract.sol"; @@ -35,11 +36,6 @@ contract PoolManagerInitializeTest is Test, Deployers, GasSnapshot { IHooks hooks ); - address ADDRESS_ZERO = address(0); - address EMPTY_HOOKS = address(0xf000000000000000000000000000000000000000); - address ALL_HOOKS = address(0xff00000000000000000000000000000000000001); - address MOCK_HOOKS = address(0xfF00000000000000000000000000000000000000); - function setUp() public { deployFreshManagerAndRouters(); (currency0, currency1) = deployMintAndApprove2Currencies(); @@ -48,7 +44,7 @@ contract PoolManagerInitializeTest is Test, Deployers, GasSnapshot { currency0: currency0, currency1: currency1, fee: 3000, - hooks: IHooks(ADDRESS_ZERO), + hooks: IHooks(Constants.ADDRESS_ZERO), tickSpacing: 60 }); } @@ -58,7 +54,7 @@ contract PoolManagerInitializeTest is Test, Deployers, GasSnapshot { sqrtPriceX96 = uint160(bound(sqrtPriceX96, TickMath.MIN_SQRT_RATIO, TickMath.MAX_SQRT_RATIO - 1)); // tested in Hooks.t.sol - key0.hooks = IHooks(ADDRESS_ZERO); + key0.hooks = IHooks(Constants.ADDRESS_ZERO); if (key0.fee & FeeLibrary.STATIC_FEE_MASK >= 1000000) { vm.expectRevert(abi.encodeWithSelector(IFees.FeeTooLarge.selector)); @@ -113,7 +109,7 @@ contract PoolManagerInitializeTest is Test, Deployers, GasSnapshot { sqrtPriceX96 = uint160(bound(sqrtPriceX96, TickMath.MIN_SQRT_RATIO, TickMath.MAX_SQRT_RATIO - 1)); address payable mockAddr = payable(address(uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG))); - address payable hookAddr = payable(MOCK_HOOKS); + address payable hookAddr = payable(Constants.MOCK_HOOKS); vm.etch(hookAddr, vm.getDeployedCode("EmptyTestHooks.sol:EmptyTestHooks")); MockContract mockContract = new MockContract(); @@ -163,7 +159,7 @@ contract PoolManagerInitializeTest is Test, Deployers, GasSnapshot { // Assumptions tested in Pool.t.sol sqrtPriceX96 = uint160(bound(sqrtPriceX96, TickMath.MIN_SQRT_RATIO, TickMath.MAX_SQRT_RATIO - 1)); - address hookEmptyAddr = EMPTY_HOOKS; + address hookEmptyAddr = Constants.EMPTY_HOOKS; MockHooks impl = new MockHooks(); vm.etch(hookEmptyAddr, address(impl).code); diff --git a/test/utils/Constants.sol b/test/utils/Constants.sol index 354a2d394..5df565aac 100644 --- a/test/utils/Constants.sol +++ b/test/utils/Constants.sol @@ -13,10 +13,16 @@ library Constants { uint128 constant MAX_UINT128 = type(uint128).max; uint160 constant MAX_UINT160 = type(uint160).max; + address constant ADDRESS_ZERO = address(0); + address constant EMPTY_HOOKS = address(0xf000000000000000000000000000000000000000); + address constant MOCK_HOOKS = address(0xfF00000000000000000000000000000000000000); + uint256 constant POOL_SLOT = 10; uint256 constant TICKS_OFFSET = 4; uint24 constant FEE_LOW = 500; uint24 constant FEE_MEDIUM = 3000; uint24 constant FEE_HIGH = 10000; + + bytes constant ZERO_BYTES = new bytes(0); } diff --git a/test/utils/Deployers.sol b/test/utils/Deployers.sol index a891bf6c5..e676b08a1 100644 --- a/test/utils/Deployers.sol +++ b/test/utils/Deployers.sol @@ -16,6 +16,7 @@ import {PoolModifyPositionTest} from "../../src/test/PoolModifyPositionTest.sol" import {PoolSwapTest} from "../../src/test/PoolSwapTest.sol"; import {PoolInitializeTest} from "../../src/test/PoolInitializeTest.sol"; import {PoolDonateTest} from "../../src/test/PoolDonateTest.sol"; +import {PoolNestedActionsTest} from "../../src/test/PoolNestedActionsTest.sol"; import {PoolTakeTest} from "../../src/test/PoolTakeTest.sol"; import { ProtocolFeeControllerTest, @@ -30,7 +31,7 @@ contract Deployers { using PoolIdLibrary for PoolKey; // Helpful test constants - bytes constant ZERO_BYTES = new bytes(0); + bytes constant ZERO_BYTES = Constants.ZERO_BYTES; uint160 constant SQRT_RATIO_1_1 = Constants.SQRT_RATIO_1_1; uint160 constant SQRT_RATIO_1_2 = Constants.SQRT_RATIO_1_2; uint160 constant SQRT_RATIO_1_4 = Constants.SQRT_RATIO_1_4; @@ -48,6 +49,7 @@ contract Deployers { PoolDonateTest donateRouter; PoolTakeTest takeRouter; PoolInitializeTest initializeRouter; + PoolNestedActionsTest nestedActionRouter; ProtocolFeeControllerTest feeController; RevertingProtocolFeeControllerTest revertingFeeController; OutOfBoundsProtocolFeeControllerTest outOfBoundsFeeController; @@ -70,6 +72,7 @@ contract Deployers { donateRouter = new PoolDonateTest(manager); takeRouter = new PoolTakeTest(manager); initializeRouter = new PoolInitializeTest(manager); + nestedActionRouter = new PoolNestedActionsTest(manager); feeController = new ProtocolFeeControllerTest(); revertingFeeController = new RevertingProtocolFeeControllerTest(); outOfBoundsFeeController = new OutOfBoundsProtocolFeeControllerTest(); @@ -150,6 +153,7 @@ contract Deployers { // sets the global currencyies and key (currency0, currency1) = deployMintAndApprove2Currencies(); (key,) = initPoolAndAddLiquidity(currency0, currency1, hooks, 3000, SQRT_RATIO_1_1, ZERO_BYTES); + nestedActionRouter.executor().setKey(key); (nativeKey,) = initPoolAndAddLiquidityETH( CurrencyLibrary.NATIVE, currency1, hooks, 3000, SQRT_RATIO_1_1, ZERO_BYTES, 1 ether ); From df58a4560a44c4f27597f7022248d4e205b86ab3 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Wed, 13 Dec 2023 15:59:03 -0300 Subject: [PATCH 07/19] Test for a nested swap --- ...swap hook, already cached dynamic fee.snap | 2 +- .../cached dynamic fee, no hooks.snap | 2 +- .forge-snapshots/donate gas with 1 token.snap | 2 +- .../donate gas with 2 tokens.snap | 2 +- .forge-snapshots/mint with empty hook.snap | 2 +- .forge-snapshots/mint with native token.snap | 2 +- .forge-snapshots/mint.snap | 2 +- .../mintWithEmptyHookEOAInitiated.snap | 2 +- .../modify position with noop.snap | 2 +- .forge-snapshots/simple swap with native.snap | 2 +- .forge-snapshots/simple swap.snap | 2 +- .forge-snapshots/simpleSwapEOAInitiated.snap | 2 +- .../simpleSwapNativeEOAInitiated.snap | 2 +- ...p against liquidity with native token.snap | 2 +- .forge-snapshots/swap against liquidity.snap | 2 +- .../swap burn claim for input.snap | 2 +- .../swap mint output as claim.snap | 2 +- .forge-snapshots/swap with dynamic fee.snap | 2 +- .forge-snapshots/swap with hooks.snap | 2 +- .forge-snapshots/swap with noop.snap | 2 +- .../update dynamic fee in before swap.snap | 2 +- src/test/PoolDonateTest.sol | 10 ++-- src/test/PoolModifyPositionTest.sol | 4 +- src/test/PoolNestedActionsTest.sol | 60 +++++++++++-------- src/test/PoolSwapTest.sol | 10 ++-- src/test/PoolTakeTest.sol | 4 +- src/test/PoolTestBase.sol | 4 +- test/Claims.t.sol | 2 +- test/NestedActions.t.sol | 7 +++ test/PoolManager.t.sol | 4 +- test/utils/Deployers.sol | 12 +++- 31 files changed, 93 insertions(+), 66 deletions(-) diff --git a/.forge-snapshots/before swap hook, already cached dynamic fee.snap b/.forge-snapshots/before swap hook, already cached dynamic fee.snap index 49651fee8..e744af944 100644 --- a/.forge-snapshots/before swap hook, already cached dynamic fee.snap +++ b/.forge-snapshots/before swap hook, already cached dynamic fee.snap @@ -1 +1 @@ -186339 \ No newline at end of file +186371 \ No newline at end of file diff --git a/.forge-snapshots/cached dynamic fee, no hooks.snap b/.forge-snapshots/cached dynamic fee, no hooks.snap index 0ca1c73f5..91a45478c 100644 --- a/.forge-snapshots/cached dynamic fee, no hooks.snap +++ b/.forge-snapshots/cached dynamic fee, no hooks.snap @@ -1 +1 @@ -140324 \ No newline at end of file +140356 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 1 token.snap b/.forge-snapshots/donate gas with 1 token.snap index 73790f19f..496f8cfa1 100644 --- a/.forge-snapshots/donate gas with 1 token.snap +++ b/.forge-snapshots/donate gas with 1 token.snap @@ -1 +1 @@ -133211 \ No newline at end of file +133243 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 2 tokens.snap b/.forge-snapshots/donate gas with 2 tokens.snap index 6c5a6b118..d856f5032 100644 --- a/.forge-snapshots/donate gas with 2 tokens.snap +++ b/.forge-snapshots/donate gas with 2 tokens.snap @@ -1 +1 @@ -178421 \ No newline at end of file +178453 \ No newline at end of file diff --git a/.forge-snapshots/mint with empty hook.snap b/.forge-snapshots/mint with empty hook.snap index 5fa2837ed..362a8928c 100644 --- a/.forge-snapshots/mint with empty hook.snap +++ b/.forge-snapshots/mint with empty hook.snap @@ -1 +1 @@ -312975 \ No newline at end of file +312991 \ No newline at end of file diff --git a/.forge-snapshots/mint with native token.snap b/.forge-snapshots/mint with native token.snap index 655b85cd6..28a16f7f2 100644 --- a/.forge-snapshots/mint with native token.snap +++ b/.forge-snapshots/mint with native token.snap @@ -1 +1 @@ -193093 \ No newline at end of file +193109 \ No newline at end of file diff --git a/.forge-snapshots/mint.snap b/.forge-snapshots/mint.snap index 95b04a94a..6f56e397c 100644 --- a/.forge-snapshots/mint.snap +++ b/.forge-snapshots/mint.snap @@ -1 +1 @@ -193035 \ No newline at end of file +193051 \ No newline at end of file diff --git a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap index efb2e0bff..e5201eeda 100644 --- a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap +++ b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap @@ -1 +1 @@ -247083 \ No newline at end of file +247099 \ No newline at end of file diff --git a/.forge-snapshots/modify position with noop.snap b/.forge-snapshots/modify position with noop.snap index 7c1d70534..57114e0b6 100644 --- a/.forge-snapshots/modify position with noop.snap +++ b/.forge-snapshots/modify position with noop.snap @@ -1 +1 @@ -53978 \ No newline at end of file +53994 \ No newline at end of file diff --git a/.forge-snapshots/simple swap with native.snap b/.forge-snapshots/simple swap with native.snap index 90b62aa48..ded864bdb 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -188866 \ No newline at end of file +188898 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index 3a15f03d9..207dd1533 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -197434 \ No newline at end of file +197466 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapEOAInitiated.snap b/.forge-snapshots/simpleSwapEOAInitiated.snap index f40b6a487..247afd567 100644 --- a/.forge-snapshots/simpleSwapEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapEOAInitiated.snap @@ -1 +1 @@ -168722 \ No newline at end of file +168754 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap index d3e4b0504..ff96fb216 100644 --- a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap @@ -1 +1 @@ -165384 \ No newline at end of file +165416 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity with native token.snap b/.forge-snapshots/swap against liquidity with native token.snap index 647f29ebc..4397e15d9 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -119494 \ No newline at end of file +119526 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index ff97d120f..e7b48f7f1 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -106985 \ No newline at end of file +107017 \ No newline at end of file diff --git a/.forge-snapshots/swap burn claim for input.snap b/.forge-snapshots/swap burn claim for input.snap index 35c806bf3..24657ba24 100644 --- a/.forge-snapshots/swap burn claim for input.snap +++ b/.forge-snapshots/swap burn claim for input.snap @@ -1 +1 @@ -126158 \ No newline at end of file +126190 \ No newline at end of file diff --git a/.forge-snapshots/swap mint output as claim.snap b/.forge-snapshots/swap mint output as claim.snap index 3b23424b0..977ecd4f7 100644 --- a/.forge-snapshots/swap mint output as claim.snap +++ b/.forge-snapshots/swap mint output as claim.snap @@ -1 +1 @@ -209649 \ No newline at end of file +209681 \ No newline at end of file diff --git a/.forge-snapshots/swap with dynamic fee.snap b/.forge-snapshots/swap with dynamic fee.snap index a0decf59e..6342b3e61 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -185440 \ No newline at end of file +185472 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index 4ebfef2b9..6aeeee515 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -106963 \ No newline at end of file +106995 \ No newline at end of file diff --git a/.forge-snapshots/swap with noop.snap b/.forge-snapshots/swap with noop.snap index 1053b45e4..b5aea2f8c 100644 --- a/.forge-snapshots/swap with noop.snap +++ b/.forge-snapshots/swap with noop.snap @@ -1 +1 @@ -47361 \ No newline at end of file +47393 \ No newline at end of file diff --git a/.forge-snapshots/update dynamic fee in before swap.snap b/.forge-snapshots/update dynamic fee in before swap.snap index cb5df9a3e..0ef257265 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -192160 \ No newline at end of file +192192 \ No newline at end of file diff --git a/src/test/PoolDonateTest.sol b/src/test/PoolDonateTest.sol index 726104d6e..e1299b17b 100644 --- a/src/test/PoolDonateTest.sol +++ b/src/test/PoolDonateTest.sol @@ -45,16 +45,18 @@ contract PoolDonateTest is PoolTestBase, Test { CallbackData memory data = abi.decode(rawData, (CallbackData)); - (,, uint256 reserveBefore0, int256 deltaBefore0) = _fetchBalances(data.key.currency0, data.sender); - (,, uint256 reserveBefore1, int256 deltaBefore1) = _fetchBalances(data.key.currency1, data.sender); + (,, uint256 reserveBefore0, int256 deltaBefore0) = + _fetchBalances(data.key.currency0, data.sender, address(this)); + (,, uint256 reserveBefore1, int256 deltaBefore1) = + _fetchBalances(data.key.currency1, data.sender, address(this)); assertEq(deltaBefore0, 0); assertEq(deltaBefore1, 0); BalanceDelta delta = manager.donate(data.key, data.amount0, data.amount1, data.hookData); - (,, uint256 reserveAfter0, int256 deltaAfter0) = _fetchBalances(data.key.currency0, data.sender); - (,, uint256 reserveAfter1, int256 deltaAfter1) = _fetchBalances(data.key.currency1, data.sender); + (,, uint256 reserveAfter0, int256 deltaAfter0) = _fetchBalances(data.key.currency0, data.sender, address(this)); + (,, uint256 reserveAfter1, int256 deltaAfter1) = _fetchBalances(data.key.currency1, data.sender, address(this)); assertEq(reserveBefore0, reserveAfter0); assertEq(reserveBefore1, reserveAfter1); diff --git a/src/test/PoolModifyPositionTest.sol b/src/test/PoolModifyPositionTest.sol index 6480730f5..780850e7d 100644 --- a/src/test/PoolModifyPositionTest.sol +++ b/src/test/PoolModifyPositionTest.sol @@ -47,8 +47,8 @@ contract PoolModifyPositionTest is Test, PoolTestBase { BalanceDelta delta = manager.modifyPosition(data.key, data.params, data.hookData); - (,,, int256 delta0) = _fetchBalances(data.key.currency0, data.sender); - (,,, int256 delta1) = _fetchBalances(data.key.currency1, data.sender); + (,,, int256 delta0) = _fetchBalances(data.key.currency0, data.sender, address(this)); + (,,, int256 delta1) = _fetchBalances(data.key.currency1, data.sender, address(this)); if (data.params.liquidityDelta > 0) { assert(delta0 > 0 || delta1 > 0 || data.key.hooks.hasPermission(Hooks.NO_OP_FLAG)); diff --git a/src/test/PoolNestedActionsTest.sol b/src/test/PoolNestedActionsTest.sol index 7dab785b0..1db0e92a2 100644 --- a/src/test/PoolNestedActionsTest.sol +++ b/src/test/PoolNestedActionsTest.sol @@ -22,14 +22,12 @@ enum Action { contract PoolNestedActionsTest is Test, ILockCallback { IPoolManager manager; NestedActionExecutor public executor; + address user; constructor(IPoolManager _manager) { manager = _manager; - executor = new NestedActionExecutor(manager); - } - - function lock(Action[] memory actions) external { - manager.lock(address(this), abi.encode(actions)); + user = msg.sender; + executor = new NestedActionExecutor(manager, user); } /// @notice Called by the pool manager on `msg.sender` when a lock is acquired @@ -46,19 +44,20 @@ contract PoolNestedActionsTest is Test, ILockCallback { function _nestedLock() internal { (address locker, address lockCaller) = manager.getLock(); assertEq(locker, address(this)); - assertEq(lockCaller, address(this)); + assertEq(lockCaller, user); vm.expectRevert(abi.encodeWithSelector(IPoolManager.LockedBy.selector, address(this))); manager.lock(address(this), ""); (locker, lockCaller) = manager.getLock(); assertEq(locker, address(this)); - assertEq(lockCaller, address(this)); + assertEq(lockCaller, user); } } contract NestedActionExecutor is Test, PoolTestBase { PoolKey internal key; + address user; error KeyNotSet(); @@ -71,7 +70,9 @@ contract NestedActionExecutor is Test, PoolTestBase { IPoolManager.SwapParams internal SWAP_PARAMS = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: Constants.SQRT_RATIO_1_2}); - constructor(IPoolManager _manager) PoolTestBase(_manager) {} + constructor(IPoolManager _manager, address _user) PoolTestBase(_manager) { + user = _user; + } function setKey(PoolKey memory _key) external { key = _key; @@ -92,36 +93,43 @@ contract NestedActionExecutor is Test, PoolTestBase { function _nestedLock() internal { (address locker, address lockCaller) = manager.getLock(); assertEq(locker, msg.sender); - assertEq(lockCaller, msg.sender); + assertEq(lockCaller, user); vm.expectRevert(abi.encodeWithSelector(IPoolManager.LockedBy.selector, msg.sender)); manager.lock(address(this), ""); (locker, lockCaller) = manager.getLock(); assertEq(locker, msg.sender); - assertEq(lockCaller, msg.sender); + assertEq(lockCaller, user); } function _swap() internal { - // swap without a lock, checking that the deltas are applied to this contract's address - (,,, int256 deltaLockerBefore0) = _fetchBalances(key.currency0, msg.sender); - (,,, int256 deltaLockerBefore1) = _fetchBalances(key.currency1, msg.sender); - (,,, int256 deltaThisBefore0) = _fetchBalances(key.currency0, address(this)); - (,,, int256 deltaThisBefore1) = _fetchBalances(key.currency1, address(this)); + (address locker, address lockCaller) = manager.getLock(); + + // swap without a lock, checking that the deltas are applied to this contract's address not the locker's address + assertTrue(locker != address(this), "Locker wrong"); + assertEq(lockCaller, user); + (,,, int256 deltaLockerBefore0) = _fetchBalances(key.currency0, user, locker); + (,,, int256 deltaLockerBefore1) = _fetchBalances(key.currency1, user, locker); + (,,, int256 deltaThisBefore0) = _fetchBalances(key.currency0, user, address(this)); + (,,, int256 deltaThisBefore1) = _fetchBalances(key.currency1, user, address(this)); BalanceDelta delta = manager.swap(key, SWAP_PARAMS, ""); - (,,, int256 deltaLockerAfter0) = _fetchBalances(key.currency0, msg.sender); - (,,, int256 deltaLockerAfter1) = _fetchBalances(key.currency1, msg.sender); - (,,, int256 deltaThisAfter0) = _fetchBalances(key.currency0, address(this)); - (,,, int256 deltaThisAfter1) = _fetchBalances(key.currency1, address(this)); - - assertEq(deltaLockerBefore0, deltaLockerAfter0); - assertEq(deltaLockerBefore1, deltaLockerAfter1); - assertEq(deltaThisBefore0 + SWAP_PARAMS.amountSpecified, deltaThisAfter0); - assertEq(deltaThisBefore1 - 98, deltaThisAfter1); - assertEq(delta.amount0(), deltaThisAfter0); - assertEq(delta.amount1(), deltaThisAfter1); + (,,, int256 deltaLockerAfter0) = _fetchBalances(key.currency0, user, locker); + (,,, int256 deltaLockerAfter1) = _fetchBalances(key.currency1, user, locker); + (,,, int256 deltaThisAfter0) = _fetchBalances(key.currency0, user, address(this)); + (,,, int256 deltaThisAfter1) = _fetchBalances(key.currency1, user, address(this)); + + assertEq(deltaLockerBefore0, deltaLockerAfter0, "Locker delta 0"); + assertEq(deltaLockerBefore1, deltaLockerAfter1, "Locker delta 1"); + assertEq(deltaThisBefore0 + SWAP_PARAMS.amountSpecified, deltaThisAfter0, "Executor delta 0"); + assertEq(deltaThisBefore1 - 98, deltaThisAfter1, "Executor delta 1"); + assertEq(delta.amount0(), deltaThisAfter0, "Swap delta 0"); + assertEq(delta.amount1(), deltaThisAfter1, "Swap delta 1"); + + _settle(key.currency0, user, int128(deltaThisAfter0), true); + _take(key.currency1, user, int128(deltaThisAfter1), true); } function _addLiquidity() internal {} diff --git a/src/test/PoolSwapTest.sol b/src/test/PoolSwapTest.sol index 01715c6b1..34a64a652 100644 --- a/src/test/PoolSwapTest.sol +++ b/src/test/PoolSwapTest.sol @@ -54,16 +54,18 @@ contract PoolSwapTest is Test, PoolTestBase { CallbackData memory data = abi.decode(rawData, (CallbackData)); - (,, uint256 reserveBefore0, int256 deltaBefore0) = _fetchBalances(data.key.currency0, data.sender); - (,, uint256 reserveBefore1, int256 deltaBefore1) = _fetchBalances(data.key.currency1, data.sender); + (,, uint256 reserveBefore0, int256 deltaBefore0) = + _fetchBalances(data.key.currency0, data.sender, address(this)); + (,, uint256 reserveBefore1, int256 deltaBefore1) = + _fetchBalances(data.key.currency1, data.sender, address(this)); assertEq(deltaBefore0, 0); assertEq(deltaBefore1, 0); BalanceDelta delta = manager.swap(data.key, data.params, data.hookData); - (,, uint256 reserveAfter0, int256 deltaAfter0) = _fetchBalances(data.key.currency0, data.sender); - (,, uint256 reserveAfter1, int256 deltaAfter1) = _fetchBalances(data.key.currency1, data.sender); + (,, uint256 reserveAfter0, int256 deltaAfter0) = _fetchBalances(data.key.currency0, data.sender, address(this)); + (,, uint256 reserveAfter1, int256 deltaAfter1) = _fetchBalances(data.key.currency1, data.sender, address(this)); assertEq(reserveBefore0, reserveAfter0); assertEq(reserveBefore1, reserveAfter1); diff --git a/src/test/PoolTakeTest.sol b/src/test/PoolTakeTest.sol index 40b763891..dd26a06ad 100644 --- a/src/test/PoolTakeTest.sol +++ b/src/test/PoolTakeTest.sol @@ -38,13 +38,13 @@ contract PoolTakeTest is Test, PoolTestBase { function _testTake(Currency currency, address sender, uint256 amount) internal { (uint256 userBalBefore, uint256 pmBalBefore, uint256 reserveBefore, int256 deltaBefore) = - _fetchBalances(currency, sender); + _fetchBalances(currency, sender, address(this)); assertEq(deltaBefore, 0); _take(currency, sender, -(amount.toInt128()), true); (uint256 userBalAfter, uint256 pmBalAfter, uint256 reserveAfter, int256 deltaAfter) = - _fetchBalances(currency, sender); + _fetchBalances(currency, sender, address(this)); assertEq(deltaAfter, amount.toInt128()); assertEq(userBalAfter - userBalBefore, amount); diff --git a/src/test/PoolTestBase.sol b/src/test/PoolTestBase.sol index 43fc61be5..4d5815400 100644 --- a/src/test/PoolTestBase.sol +++ b/src/test/PoolTestBase.sol @@ -38,7 +38,7 @@ abstract contract PoolTestBase is ILockCallback { } } - function _fetchBalances(Currency currency, address user) + function _fetchBalances(Currency currency, address user, address deltaHolder) internal view returns (uint256 userBalance, uint256 poolBalance, uint256 reserves, int256 delta) @@ -46,6 +46,6 @@ abstract contract PoolTestBase is ILockCallback { userBalance = currency.balanceOf(user); poolBalance = currency.balanceOf(address(manager)); reserves = manager.reservesOf(currency); - delta = manager.currencyDelta(address(this), currency); + delta = manager.currencyDelta(deltaHolder, currency); } } diff --git a/test/Claims.t.sol b/test/Claims.t.sol index 436c671ea..2395fda50 100644 --- a/test/Claims.t.sol +++ b/test/Claims.t.sol @@ -18,7 +18,7 @@ contract ClaimsTest is Test, Deployers { event Transfer(address indexed from, address indexed to, Currency indexed currency, uint256 amount); function setUp() public { - (currency0, currency1) = deployMintAndApprove2Currencies(); + (currency0, currency1) = deployAndMint2Currencies(); } function testCanBurn(uint256 amount) public { diff --git a/test/NestedActions.t.sol b/test/NestedActions.t.sol index 411e433bc..59102ad9c 100644 --- a/test/NestedActions.t.sol +++ b/test/NestedActions.t.sol @@ -8,7 +8,14 @@ import {Action} from "../src/test/PoolNestedActionsTest.sol"; import {IHooks} from "../src/interfaces/IHooks.sol"; contract NestedActions is Test, Deployers, GasSnapshot { + Action[] actions; + function setUp() public { initializeManagerRoutersAndPoolsWithLiq(IHooks(address(0))); } + + function test_nestedSwap() public { + actions = [Action.SWAP_AND_SETTLE]; + manager.lock(address(nestedActionRouter), abi.encode(actions)); + } } diff --git a/test/PoolManager.t.sol b/test/PoolManager.t.sol index 9680112b7..5dd1b60a2 100644 --- a/test/PoolManager.t.sol +++ b/test/PoolManager.t.sol @@ -1126,12 +1126,12 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function test_lock_cannotBeCalledTwiceByLocker() public { actions = [Action.NESTED_SELF_LOCK]; - nestedActionRouter.lock(actions); + manager.lock(address(nestedActionRouter), abi.encode(actions)); } function test_lock_cannotBeCalledTwiceByDifferentLockers() public { actions = [Action.NESTED_EXECUTOR_LOCK]; - nestedActionRouter.lock(actions); + manager.lock(address(nestedActionRouter), abi.encode(actions)); } // function testExtsloadForPoolPrice() public { diff --git a/test/utils/Deployers.sol b/test/utils/Deployers.sol index e676b08a1..63584fc46 100644 --- a/test/utils/Deployers.sol +++ b/test/utils/Deployers.sol @@ -82,15 +82,18 @@ contract Deployers { manager.setProtocolFeeController(feeController); } + // You must have first initialised the routers with deployFreshManagerAndRouters + // If you only need the currencies (and not approvals) call deployAndMint2Currencies function deployMintAndApprove2Currencies() internal returns (Currency, Currency) { MockERC20[] memory tokens = deployTokens(2, 2 ** 255); - address[5] memory toApprove = [ + address[6] memory toApprove = [ address(swapRouter), address(modifyPositionRouter), address(donateRouter), address(takeRouter), - address(initializeRouter) + address(initializeRouter), + address(nestedActionRouter.executor()) ]; for (uint256 i = 0; i < toApprove.length; i++) { @@ -101,6 +104,11 @@ contract Deployers { return SortTokens.sort(tokens[0], tokens[1]); } + function deployAndMint2Currencies() internal returns (Currency, Currency) { + MockERC20[] memory tokens = deployTokens(2, 2 ** 255); + return SortTokens.sort(tokens[0], tokens[1]); + } + function deployTokens(uint8 count, uint256 totalSupply) internal returns (MockERC20[] memory tokens) { tokens = new MockERC20[](count); for (uint8 i = 0; i < count; i++) { From 99243a1484c6296ecc79ca42f055cf7bc98e1de2 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Thu, 14 Dec 2023 12:30:38 -0300 Subject: [PATCH 08/19] Tests for nested function calls --- src/test/PoolNestedActionsTest.sol | 89 ++++++++++++++++++++++++++++-- test/NestedActions.t.sol | 17 ++++++ 2 files changed, 101 insertions(+), 5 deletions(-) diff --git a/src/test/PoolNestedActionsTest.sol b/src/test/PoolNestedActionsTest.sol index 1db0e92a2..ebee5af1a 100644 --- a/src/test/PoolNestedActionsTest.sol +++ b/src/test/PoolNestedActionsTest.sol @@ -70,6 +70,9 @@ contract NestedActionExecutor is Test, PoolTestBase { IPoolManager.SwapParams internal SWAP_PARAMS = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: Constants.SQRT_RATIO_1_2}); + uint256 internal DONATE_AMOUNT0 = 12345e6; + uint256 internal DONATE_AMOUNT1 = 98765e4; + constructor(IPoolManager _manager, address _user) PoolTestBase(_manager) { user = _user; } @@ -105,10 +108,9 @@ contract NestedActionExecutor is Test, PoolTestBase { function _swap() internal { (address locker, address lockCaller) = manager.getLock(); - - // swap without a lock, checking that the deltas are applied to this contract's address not the locker's address assertTrue(locker != address(this), "Locker wrong"); assertEq(lockCaller, user); + (,,, int256 deltaLockerBefore0) = _fetchBalances(key.currency0, user, locker); (,,, int256 deltaLockerBefore1) = _fetchBalances(key.currency1, user, locker); (,,, int256 deltaThisBefore0) = _fetchBalances(key.currency0, user, address(this)); @@ -132,9 +134,86 @@ contract NestedActionExecutor is Test, PoolTestBase { _take(key.currency1, user, int128(deltaThisAfter1), true); } - function _addLiquidity() internal {} - function _removeLiquidity() internal {} - function _donate() internal {} + function _addLiquidity() internal { + (address locker, address lockCaller) = manager.getLock(); + assertTrue(locker != address(this), "Locker wrong"); + assertEq(lockCaller, user); + + (,,, int256 deltaLockerBefore0) = _fetchBalances(key.currency0, user, locker); + (,,, int256 deltaLockerBefore1) = _fetchBalances(key.currency1, user, locker); + (,,, int256 deltaThisBefore0) = _fetchBalances(key.currency0, user, address(this)); + (,,, int256 deltaThisBefore1) = _fetchBalances(key.currency1, user, address(this)); + + BalanceDelta delta = manager.modifyPosition(key, ADD_LIQ_PARAMS, ""); + + (,,, int256 deltaLockerAfter0) = _fetchBalances(key.currency0, user, locker); + (,,, int256 deltaLockerAfter1) = _fetchBalances(key.currency1, user, locker); + (,,, int256 deltaThisAfter0) = _fetchBalances(key.currency0, user, address(this)); + (,,, int256 deltaThisAfter1) = _fetchBalances(key.currency1, user, address(this)); + + assertEq(deltaLockerBefore0, deltaLockerAfter0, "Locker delta 0"); + assertEq(deltaLockerBefore1, deltaLockerAfter1, "Locker delta 1"); + assertEq(deltaThisBefore0 + delta.amount0(), deltaThisAfter0, "Executor delta 0"); + assertEq(deltaThisBefore1 + delta.amount1(), deltaThisAfter1, "Executor delta 1"); + + _settle(key.currency0, user, int128(deltaThisAfter0), true); + _settle(key.currency1, user, int128(deltaThisAfter1), true); + } + + // cannot remove non-existent liquidity - need to perform an add before this removal + function _removeLiquidity() internal { + (address locker, address lockCaller) = manager.getLock(); + assertTrue(locker != address(this), "Locker wrong"); + assertEq(lockCaller, user); + + (,,, int256 deltaLockerBefore0) = _fetchBalances(key.currency0, user, locker); + (,,, int256 deltaLockerBefore1) = _fetchBalances(key.currency1, user, locker); + (,,, int256 deltaThisBefore0) = _fetchBalances(key.currency0, user, address(this)); + (,,, int256 deltaThisBefore1) = _fetchBalances(key.currency1, user, address(this)); + + BalanceDelta delta = manager.modifyPosition(key, REMOVE_LIQ_PARAMS, ""); + + (,,, int256 deltaLockerAfter0) = _fetchBalances(key.currency0, user, locker); + (,,, int256 deltaLockerAfter1) = _fetchBalances(key.currency1, user, locker); + (,,, int256 deltaThisAfter0) = _fetchBalances(key.currency0, user, address(this)); + (,,, int256 deltaThisAfter1) = _fetchBalances(key.currency1, user, address(this)); + + assertEq(deltaLockerBefore0, deltaLockerAfter0, "Locker delta 0"); + assertEq(deltaLockerBefore1, deltaLockerAfter1, "Locker delta 1"); + assertEq(deltaThisBefore0 + delta.amount0(), deltaThisAfter0, "Executor delta 0"); + assertEq(deltaThisBefore1 + delta.amount1(), deltaThisAfter1, "Executor delta 1"); + + _take(key.currency0, user, int128(deltaThisAfter0), true); + _take(key.currency1, user, int128(deltaThisAfter1), true); + } + + function _donate() internal { + (address locker, address lockCaller) = manager.getLock(); + assertTrue(locker != address(this), "Locker wrong"); + assertEq(lockCaller, user); + + (,,, int256 deltaLockerBefore0) = _fetchBalances(key.currency0, user, locker); + (,,, int256 deltaLockerBefore1) = _fetchBalances(key.currency1, user, locker); + (,,, int256 deltaThisBefore0) = _fetchBalances(key.currency0, user, address(this)); + (,,, int256 deltaThisBefore1) = _fetchBalances(key.currency1, user, address(this)); + + BalanceDelta delta = manager.donate(key, DONATE_AMOUNT0, DONATE_AMOUNT1, ""); + + (,,, int256 deltaLockerAfter0) = _fetchBalances(key.currency0, user, locker); + (,,, int256 deltaLockerAfter1) = _fetchBalances(key.currency1, user, locker); + (,,, int256 deltaThisAfter0) = _fetchBalances(key.currency0, user, address(this)); + (,,, int256 deltaThisAfter1) = _fetchBalances(key.currency1, user, address(this)); + + assertEq(deltaLockerBefore0, deltaLockerAfter0, "Locker delta 0"); + assertEq(deltaLockerBefore1, deltaLockerAfter1, "Locker delta 1"); + assertEq(deltaThisBefore0 + int256(DONATE_AMOUNT0), deltaThisAfter0, "Executor delta 0"); + assertEq(deltaThisBefore1 + int256(DONATE_AMOUNT1), deltaThisAfter1, "Executor delta 1"); + assertEq(delta.amount0(), int256(DONATE_AMOUNT0), "Donate delta 0"); + assertEq(delta.amount1(), int256(DONATE_AMOUNT1), "Donate delta 1"); + + _settle(key.currency0, user, int128(deltaThisAfter0), true); + _settle(key.currency1, user, int128(deltaThisAfter1), true); + } // This will never actually be used - its just to allow us to use the PoolTestBase helper contact function lockAcquired(address, bytes calldata) external pure override returns (bytes memory) { diff --git a/test/NestedActions.t.sol b/test/NestedActions.t.sol index 59102ad9c..e2e8cd5fc 100644 --- a/test/NestedActions.t.sol +++ b/test/NestedActions.t.sol @@ -14,8 +14,25 @@ contract NestedActions is Test, Deployers, GasSnapshot { initializeManagerRoutersAndPoolsWithLiq(IHooks(address(0))); } + // Asserts and expected outcomes are tested inside the nestedActionRouter + function test_nestedSwap() public { actions = [Action.SWAP_AND_SETTLE]; manager.lock(address(nestedActionRouter), abi.encode(actions)); } + + function test_nestedAddLiquidity() public { + actions = [Action.ADD_LIQ_AND_SETTLE]; + manager.lock(address(nestedActionRouter), abi.encode(actions)); + } + + function test_nestedRemoveLiquidity() public { + actions = [Action.ADD_LIQ_AND_SETTLE, Action.REMOVE_LIQ_AND_SETTLE]; + manager.lock(address(nestedActionRouter), abi.encode(actions)); + } + + function test_nestedDonate() public { + actions = [Action.DONATE_AND_SETTLE]; + manager.lock(address(nestedActionRouter), abi.encode(actions)); + } } From 56f60441fd8a8d2dd53e54a66c3070135920a8ba Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Mon, 18 Dec 2023 12:47:11 -0300 Subject: [PATCH 09/19] snapshots --- .forge-snapshots/addLiquidity with empty hook.snap | 2 +- .forge-snapshots/addLiquidity with native token.snap | 2 +- .forge-snapshots/addLiquidity.snap | 2 +- .../before swap hook, already cached dynamic fee.snap | 2 +- .forge-snapshots/cached dynamic fee, no hooks.snap | 2 +- .forge-snapshots/donate gas with 1 token.snap | 2 +- .forge-snapshots/donate gas with 2 tokens.snap | 2 +- .forge-snapshots/gas overhead of no-op lock.snap | 2 +- .forge-snapshots/initialize.snap | 2 +- .forge-snapshots/mintWithEmptyHookEOAInitiated.snap | 2 +- .forge-snapshots/modify position with noop.snap | 2 +- .forge-snapshots/poolManager bytecode size.snap | 2 +- .forge-snapshots/removeLiquidity with empty hook.snap | 2 +- .forge-snapshots/removeLiquidity with native token.snap | 2 +- .forge-snapshots/removeLiquidity.snap | 2 +- .forge-snapshots/simple swap with native.snap | 2 +- .forge-snapshots/simple swap.snap | 2 +- .forge-snapshots/simpleSwapEOAInitiated.snap | 2 +- .forge-snapshots/simpleSwapNativeEOAInitiated.snap | 2 +- .forge-snapshots/swap against liquidity with native token.snap | 2 +- .forge-snapshots/swap against liquidity.snap | 2 +- .forge-snapshots/swap burn 6909 for input.snap | 2 +- .forge-snapshots/swap burn native 6909 for input.snap | 2 +- .forge-snapshots/swap mint native output as 6909.snap | 2 +- .forge-snapshots/swap mint output as 6909.snap | 2 +- .forge-snapshots/swap with dynamic fee.snap | 2 +- .forge-snapshots/swap with hooks.snap | 2 +- .forge-snapshots/swap with noop.snap | 2 +- .forge-snapshots/update dynamic fee in before swap.snap | 2 +- 29 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.forge-snapshots/addLiquidity with empty hook.snap b/.forge-snapshots/addLiquidity with empty hook.snap index 4b2bb2fdf..79e9c2ed3 100644 --- a/.forge-snapshots/addLiquidity with empty hook.snap +++ b/.forge-snapshots/addLiquidity with empty hook.snap @@ -1 +1 @@ -313066 +313136 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity with native token.snap b/.forge-snapshots/addLiquidity with native token.snap index 93ddda50d..3d07131b6 100644 --- a/.forge-snapshots/addLiquidity with native token.snap +++ b/.forge-snapshots/addLiquidity with native token.snap @@ -1 +1 @@ -200111 +193250 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity.snap b/.forge-snapshots/addLiquidity.snap index 62cee4de8..6af02359a 100644 --- a/.forge-snapshots/addLiquidity.snap +++ b/.forge-snapshots/addLiquidity.snap @@ -1 +1 @@ -200079 +193218 \ No newline at end of file diff --git a/.forge-snapshots/before swap hook, already cached dynamic fee.snap b/.forge-snapshots/before swap hook, already cached dynamic fee.snap index 075530451..d739f8c1b 100644 --- a/.forge-snapshots/before swap hook, already cached dynamic fee.snap +++ b/.forge-snapshots/before swap hook, already cached dynamic fee.snap @@ -1 +1 @@ -194367 +186327 \ No newline at end of file diff --git a/.forge-snapshots/cached dynamic fee, no hooks.snap b/.forge-snapshots/cached dynamic fee, no hooks.snap index 75d8913df..9f57e41db 100644 --- a/.forge-snapshots/cached dynamic fee, no hooks.snap +++ b/.forge-snapshots/cached dynamic fee, no hooks.snap @@ -1 +1 @@ -146705 +140305 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 1 token.snap b/.forge-snapshots/donate gas with 1 token.snap index 108aa608b..ff057d01e 100644 --- a/.forge-snapshots/donate gas with 1 token.snap +++ b/.forge-snapshots/donate gas with 1 token.snap @@ -1 +1 @@ -137750 +133155 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 2 tokens.snap b/.forge-snapshots/donate gas with 2 tokens.snap index fa69246bf..6e44c51c2 100644 --- a/.forge-snapshots/donate gas with 2 tokens.snap +++ b/.forge-snapshots/donate gas with 2 tokens.snap @@ -1 +1 @@ -185163 +178356 \ No newline at end of file diff --git a/.forge-snapshots/gas overhead of no-op lock.snap b/.forge-snapshots/gas overhead of no-op lock.snap index 7c187c391..f6afc4b52 100644 --- a/.forge-snapshots/gas overhead of no-op lock.snap +++ b/.forge-snapshots/gas overhead of no-op lock.snap @@ -1 +1 @@ -15223 +15119 \ No newline at end of file diff --git a/.forge-snapshots/initialize.snap b/.forge-snapshots/initialize.snap index 256e73e37..7da428eaf 100644 --- a/.forge-snapshots/initialize.snap +++ b/.forge-snapshots/initialize.snap @@ -1 +1 @@ -74274 +73279 \ No newline at end of file diff --git a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap index e04c9baeb..ef0a0e6db 100644 --- a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap +++ b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap @@ -1 +1 @@ -255189 +247260 \ No newline at end of file diff --git a/.forge-snapshots/modify position with noop.snap b/.forge-snapshots/modify position with noop.snap index 86304fed0..f1273a4d9 100644 --- a/.forge-snapshots/modify position with noop.snap +++ b/.forge-snapshots/modify position with noop.snap @@ -1 +1 @@ -58076 +53999 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index dce960779..bd9b8d797 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -25102 +24674 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with empty hook.snap b/.forge-snapshots/removeLiquidity with empty hook.snap index 9479e469d..d3458b39a 100644 --- a/.forge-snapshots/removeLiquidity with empty hook.snap +++ b/.forge-snapshots/removeLiquidity with empty hook.snap @@ -1 +1 @@ -106952 +100091 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with native token.snap b/.forge-snapshots/removeLiquidity with native token.snap index cea785cd4..47b3444dd 100644 --- a/.forge-snapshots/removeLiquidity with native token.snap +++ b/.forge-snapshots/removeLiquidity with native token.snap @@ -1 +1 @@ -208312 +201451 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity.snap b/.forge-snapshots/removeLiquidity.snap index 28c001393..1aa3020ff 100644 --- a/.forge-snapshots/removeLiquidity.snap +++ b/.forge-snapshots/removeLiquidity.snap @@ -1 +1 @@ -204602 +197741 \ No newline at end of file diff --git a/.forge-snapshots/simple swap with native.snap b/.forge-snapshots/simple swap with native.snap index e91ee7d11..3cf5fc30c 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -195676 +188847 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index 85d1947de..a9920c39e 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -204244 +197415 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapEOAInitiated.snap b/.forge-snapshots/simpleSwapEOAInitiated.snap index 20fdb6b72..984d5c7d7 100644 --- a/.forge-snapshots/simpleSwapEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapEOAInitiated.snap @@ -1 +1 @@ -174149 +167308 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap index 6b59faba1..f976859af 100644 --- a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap @@ -1 +1 @@ -172798 +165924 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity with native token.snap b/.forge-snapshots/swap against liquidity with native token.snap index f1e6c84d3..e62462eb7 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -126304 +119475 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index 1c4234f99..ed6a63a18 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -113795 +106966 \ No newline at end of file diff --git a/.forge-snapshots/swap burn 6909 for input.snap b/.forge-snapshots/swap burn 6909 for input.snap index 3afc4c5e8..8ce54e1ea 100644 --- a/.forge-snapshots/swap burn 6909 for input.snap +++ b/.forge-snapshots/swap burn 6909 for input.snap @@ -1 +1 @@ -133995 \ No newline at end of file +127166 \ No newline at end of file diff --git a/.forge-snapshots/swap burn native 6909 for input.snap b/.forge-snapshots/swap burn native 6909 for input.snap index 1d3904e93..a63827696 100644 --- a/.forge-snapshots/swap burn native 6909 for input.snap +++ b/.forge-snapshots/swap burn native 6909 for input.snap @@ -1 +1 @@ -129926 \ No newline at end of file +123097 \ No newline at end of file diff --git a/.forge-snapshots/swap mint native output as 6909.snap b/.forge-snapshots/swap mint native output as 6909.snap index d1c7601b0..795321aee 100644 --- a/.forge-snapshots/swap mint native output as 6909.snap +++ b/.forge-snapshots/swap mint native output as 6909.snap @@ -1 +1 @@ -198205 \ No newline at end of file +191331 \ No newline at end of file diff --git a/.forge-snapshots/swap mint output as 6909.snap b/.forge-snapshots/swap mint output as 6909.snap index 4dc0c4c51..fbc2e5c73 100644 --- a/.forge-snapshots/swap mint output as 6909.snap +++ b/.forge-snapshots/swap mint output as 6909.snap @@ -1 +1 @@ -215075 \ No newline at end of file +208201 \ No newline at end of file diff --git a/.forge-snapshots/swap with dynamic fee.snap b/.forge-snapshots/swap with dynamic fee.snap index 743acaa21..b24746598 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -193468 +185428 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index f3ff2b1fe..3fee59267 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -113773 +106944 \ No newline at end of file diff --git a/.forge-snapshots/swap with noop.snap b/.forge-snapshots/swap with noop.snap index 0216360d7..54b98947e 100644 --- a/.forge-snapshots/swap with noop.snap +++ b/.forge-snapshots/swap with noop.snap @@ -1 +1 @@ -51425 +47380 \ No newline at end of file diff --git a/.forge-snapshots/update dynamic fee in before swap.snap b/.forge-snapshots/update dynamic fee in before swap.snap index ce9b7091f..bfe5196db 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -200232 +192148 \ No newline at end of file From 6ce20c4086ecfa67362515cf06db3c3128df380f Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Wed, 31 Jan 2024 12:46:38 -0300 Subject: [PATCH 10/19] Separate libs, rename error --- .../poolManager bytecode size.snap | 2 +- .forge-snapshots/simpleSwapEOAInitiated.snap | 2 +- src/PoolManager.sol | 11 ++--- src/interfaces/IPoolManager.sol | 3 +- src/libraries/Locker.sol | 32 +-------------- src/libraries/NonZeroDeltaCount.sol | 39 ++++++++++++++++++ src/test/PoolNestedActionsTest.sol | 4 +- test/Locker.t.sol | 33 +-------------- test/NonZeroDeltaCount.t.sol | 41 +++++++++++++++++++ 9 files changed, 93 insertions(+), 74 deletions(-) create mode 100644 src/libraries/NonZeroDeltaCount.sol create mode 100644 test/NonZeroDeltaCount.t.sol diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index bd9b8d797..9e4e8cc2d 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -24674 \ No newline at end of file +24654 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapEOAInitiated.snap b/.forge-snapshots/simpleSwapEOAInitiated.snap index 984d5c7d7..bedcd8a46 100644 --- a/.forge-snapshots/simpleSwapEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapEOAInitiated.snap @@ -1 +1 @@ -167308 \ No newline at end of file +167275 \ No newline at end of file diff --git a/src/PoolManager.sol b/src/PoolManager.sol index 6563b353c..460be3c93 100644 --- a/src/PoolManager.sol +++ b/src/PoolManager.sol @@ -20,6 +20,7 @@ import {ERC6909Claims} from "./ERC6909Claims.sol"; import {PoolId, PoolIdLibrary} from "./types/PoolId.sol"; import {BalanceDelta, BalanceDeltaLibrary} from "./types/BalanceDelta.sol"; import {Locker} from "./libraries/Locker.sol"; +import {NonZeroDeltaCount} from "./libraries/NonZeroDeltaCount.sol"; import {PoolGetters} from "./libraries/PoolGetters.sol"; /// @notice Holds the state for all pools @@ -130,14 +131,14 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { /// @inheritdoc IPoolManager function lock(address lockTarget, bytes calldata data) external payable override returns (bytes memory result) { // Get the lock caller because thats an EOA and is not user-controlable - if (Locker.isLocked()) revert LockedBy(Locker.getLocker()); + if (Locker.isLocked()) revert AlreadyLocked(); Locker.setLockerAndCaller(lockTarget, msg.sender); // the caller does everything in this callback, including paying what they owe via calls to settle result = ILockCallback(lockTarget).lockAcquired(msg.sender, data); - if (Locker.nonzeroDeltaCount() != 0) revert CurrencyNotSettled(); + if (NonZeroDeltaCount.read() != 0) revert CurrencyNotSettled(); Locker.clearLockerAndCaller(); } @@ -149,9 +150,9 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { unchecked { if (next == 0) { - Locker.decrementNonzeroDeltaCount(); + NonZeroDeltaCount.decrement(); } else if (current == 0) { - Locker.incrementNonzeroDeltaCount(); + NonZeroDeltaCount.increment(); } } @@ -331,7 +332,7 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { } function getLockNonzeroDeltaCount() external view returns (uint256 _nonzeroDeltaCount) { - return Locker.nonzeroDeltaCount(); + return NonZeroDeltaCount.read(); } function getPoolTickInfo(PoolId id, int24 tick) external view returns (Pool.TickInfo memory) { diff --git a/src/interfaces/IPoolManager.sol b/src/interfaces/IPoolManager.sol index b6cab3ba5..f198a8433 100644 --- a/src/interfaces/IPoolManager.sol +++ b/src/interfaces/IPoolManager.sol @@ -23,8 +23,7 @@ interface IPoolManager is IFees, IERC6909Claims { error PoolNotInitialized(); /// @notice Thrown when lock is called, but a lock is already open - /// @param locker The current locker - error LockedBy(address locker); + error AlreadyLocked(); /// @notice Thrown when a function is called outside of a lock error ManagerNotLocked(); diff --git a/src/libraries/Locker.sol b/src/libraries/Locker.sol index c37ab411c..e0386fb91 100644 --- a/src/libraries/Locker.sol +++ b/src/libraries/Locker.sol @@ -4,16 +4,13 @@ pragma solidity ^0.8.20; import {IHooks} from "../interfaces/IHooks.sol"; /// @notice This is a temporary library that allows us to use transient storage (tstore/tload) -/// for the lockers array and nonzero delta count. +/// for the lockers array /// TODO: This library can be deleted when we have the transient keyword support in solidity. library Locker { // The slot holding the locker, transiently, and the lock caller in the next slot uint256 constant LOCKER_SLOT = uint256(keccak256("Locker")) - 2; uint256 constant LOCK_CALLER_SLOT = LOCKER_SLOT + 1; - // The slot holding the number of nonzero deltas. - uint256 constant NONZERO_DELTA_COUNT = uint256(keccak256("NonzeroDeltaCount")) - 1; - function setLockerAndCaller(address locker, address lockCaller) internal { uint256 slot = LOCKER_SLOT; @@ -51,31 +48,4 @@ library Locker { locker := tload(slot) } } - - function nonzeroDeltaCount() internal view returns (uint256 count) { - uint256 slot = NONZERO_DELTA_COUNT; - assembly { - count := tload(slot) - } - } - - function incrementNonzeroDeltaCount() internal { - uint256 slot = NONZERO_DELTA_COUNT; - assembly { - let count := tload(slot) - count := add(count, 1) - tstore(slot, count) - } - } - - /// @notice Potential to underflow. - /// Current usage ensures this will not happen because we call decrememnt with known boundaries (only up to the number of times we call increment). - function decrementNonzeroDeltaCount() internal { - uint256 slot = NONZERO_DELTA_COUNT; - assembly { - let count := tload(slot) - count := sub(count, 1) - tstore(slot, count) - } - } } diff --git a/src/libraries/NonZeroDeltaCount.sol b/src/libraries/NonZeroDeltaCount.sol new file mode 100644 index 000000000..b1d31ca7d --- /dev/null +++ b/src/libraries/NonZeroDeltaCount.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import {IHooks} from "../interfaces/IHooks.sol"; + +/// @notice This is a temporary library that allows us to use transient storage (tstore/tload) +/// for the nonzero delta count. +/// TODO: This library can be deleted when we have the transient keyword support in solidity. +library NonZeroDeltaCount { + // The slot holding the number of nonzero deltas. + uint256 constant NONZERO_DELTA_COUNT = uint256(keccak256("NonzeroDeltaCount")) - 1; + + function read() internal view returns (uint256 count) { + uint256 slot = NONZERO_DELTA_COUNT; + assembly { + count := tload(slot) + } + } + + function increment() internal { + uint256 slot = NONZERO_DELTA_COUNT; + assembly { + let count := tload(slot) + count := add(count, 1) + tstore(slot, count) + } + } + + /// @notice Potential to underflow. + /// Current usage ensures this will not happen because we call decrememnt with known boundaries (only up to the number of times we call increment). + function decrement() internal { + uint256 slot = NONZERO_DELTA_COUNT; + assembly { + let count := tload(slot) + count := sub(count, 1) + tstore(slot, count) + } + } +} diff --git a/src/test/PoolNestedActionsTest.sol b/src/test/PoolNestedActionsTest.sol index 29fb62829..c96ef1a6d 100644 --- a/src/test/PoolNestedActionsTest.sol +++ b/src/test/PoolNestedActionsTest.sol @@ -46,7 +46,7 @@ contract PoolNestedActionsTest is Test, ILockCallback { assertEq(locker, address(this)); assertEq(lockCaller, user); - vm.expectRevert(abi.encodeWithSelector(IPoolManager.LockedBy.selector, address(this))); + vm.expectRevert(abi.encodeWithSelector(IPoolManager.AlreadyLocked.selector)); manager.lock(address(this), ""); (locker, lockCaller) = manager.getLock(); @@ -98,7 +98,7 @@ contract NestedActionExecutor is Test, PoolTestBase { assertEq(locker, msg.sender); assertEq(lockCaller, user); - vm.expectRevert(abi.encodeWithSelector(IPoolManager.LockedBy.selector, msg.sender)); + vm.expectRevert(abi.encodeWithSelector(IPoolManager.AlreadyLocked.selector)); manager.lock(address(this), ""); (locker, lockCaller) = manager.getLock(); diff --git a/test/Locker.t.sol b/test/Locker.t.sol index fc9647cb5..69cb23152 100644 --- a/test/Locker.t.sol +++ b/test/Locker.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {Locker} from "../src/libraries/Locker.sol"; -contract CurrentHookAddressTest is Test { +contract LockerTest is Test { address constant ADDRESS_AS = 0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa; address constant ADDRESS_BS = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; @@ -47,35 +47,4 @@ contract CurrentHookAddressTest is Test { assertFalse(Locker.isLocked()); } - - function test_incrementNonzeroDeltaCount() public { - Locker.incrementNonzeroDeltaCount(); - assertEq(Locker.nonzeroDeltaCount(), 1); - } - - function test_decrementNonzeroDeltaCount() public { - Locker.incrementNonzeroDeltaCount(); - Locker.decrementNonzeroDeltaCount(); - assertEq(Locker.nonzeroDeltaCount(), 0); - } - - // Reading from right to left. Bit of 0: call increase. Bit of 1: call decrease. - // The library allows over over/underflow so we dont check for that here - function test_nonZeroDeltaCount_fuzz(uint256 instructions) public { - uint256 expectedCount; - for (uint256 i = 0; i < 256; i++) { - if ((instructions & (1 << i)) == 0) { - Locker.incrementNonzeroDeltaCount(); - unchecked { - expectedCount++; - } - } else { - Locker.decrementNonzeroDeltaCount(); - unchecked { - expectedCount--; - } - } - assertEq(Locker.nonzeroDeltaCount(), expectedCount); - } - } } diff --git a/test/NonZeroDeltaCount.t.sol b/test/NonZeroDeltaCount.t.sol new file mode 100644 index 000000000..0ae1820a3 --- /dev/null +++ b/test/NonZeroDeltaCount.t.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; +import {NonZeroDeltaCount} from "../src/libraries/NonZeroDeltaCount.sol"; + +contract NonZeroDeltaCountTest is Test { + address constant ADDRESS_AS = 0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa; + address constant ADDRESS_BS = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; + + function test_incrementNonzeroDeltaCount() public { + NonZeroDeltaCount.increment(); + assertEq(NonZeroDeltaCount.read(), 1); + } + + function test_decrementNonzeroDeltaCount() public { + NonZeroDeltaCount.increment(); + NonZeroDeltaCount.decrement(); + assertEq(NonZeroDeltaCount.read(), 0); + } + + // Reading from right to left. Bit of 0: call increase. Bit of 1: call decrease. + // The library allows over over/underflow so we dont check for that here + function test_nonZeroDeltaCount_fuzz(uint256 instructions) public { + uint256 expectedCount; + for (uint256 i = 0; i < 256; i++) { + if ((instructions & (1 << i)) == 0) { + NonZeroDeltaCount.increment(); + unchecked { + expectedCount++; + } + } else { + NonZeroDeltaCount.decrement(); + unchecked { + expectedCount--; + } + } + assertEq(NonZeroDeltaCount.read(), expectedCount); + } + } +} From c802859132c18b9d6cc4923a120fdc963140452a Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Mon, 12 Feb 2024 16:45:20 -0300 Subject: [PATCH 11/19] Remove lock caller --- .../addLiquidity with empty hook.snap | 2 +- .../addLiquidity with native token.snap | 2 +- .forge-snapshots/addLiquidity.snap | 2 +- ...swap hook, already cached dynamic fee.snap | 2 +- .../cached dynamic fee, no hooks.snap | 2 +- .forge-snapshots/donate gas with 1 token.snap | 2 +- .../donate gas with 2 tokens.snap | 2 +- .../gas overhead of no-op lock.snap | 2 +- .forge-snapshots/initialize.snap | 2 +- .../mintWithEmptyHookEOAInitiated.snap | 2 +- .../modify position with noop.snap | 2 +- .../poolManager bytecode size.snap | 2 +- .../removeLiquidity with empty hook.snap | 2 +- .../removeLiquidity with native token.snap | 2 +- .forge-snapshots/removeLiquidity.snap | 2 +- .forge-snapshots/simple swap with native.snap | 2 +- .forge-snapshots/simple swap.snap | 2 +- .forge-snapshots/simpleSwapEOAInitiated.snap | 2 +- .../simpleSwapNativeEOAInitiated.snap | 2 +- ...p against liquidity with native token.snap | 2 +- .forge-snapshots/swap against liquidity.snap | 2 +- .../swap burn 6909 for input.snap | 2 +- .../swap burn native 6909 for input.snap | 2 +- .../swap mint native output as 6909.snap | 2 +- .../swap mint output as 6909.snap | 2 +- .forge-snapshots/swap with dynamic fee.snap | 2 +- .forge-snapshots/swap with hooks.snap | 2 +- .forge-snapshots/swap with noop.snap | 2 +- .../update dynamic fee in before swap.snap | 2 +- src/PoolManager.sol | 8 ++-- src/interfaces/IPoolManager.sol | 4 +- src/libraries/Locker.sol | 25 +++++------- src/test/PoolNestedActionsTest.sol | 24 ++++-------- test/Locker.t.sol | 38 +++++++++---------- 34 files changed, 69 insertions(+), 88 deletions(-) diff --git a/.forge-snapshots/addLiquidity with empty hook.snap b/.forge-snapshots/addLiquidity with empty hook.snap index 9a19fe0ce..cfc6e3e4e 100644 --- a/.forge-snapshots/addLiquidity with empty hook.snap +++ b/.forge-snapshots/addLiquidity with empty hook.snap @@ -1 +1 @@ -313482 \ No newline at end of file +313034 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity with native token.snap b/.forge-snapshots/addLiquidity with native token.snap index 15301c17e..de7c2ff33 100644 --- a/.forge-snapshots/addLiquidity with native token.snap +++ b/.forge-snapshots/addLiquidity with native token.snap @@ -1 +1 @@ -193637 \ No newline at end of file +193189 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity.snap b/.forge-snapshots/addLiquidity.snap index 07a0648b1..1750a6692 100644 --- a/.forge-snapshots/addLiquidity.snap +++ b/.forge-snapshots/addLiquidity.snap @@ -1 +1 @@ -193615 \ No newline at end of file +193167 \ No newline at end of file diff --git a/.forge-snapshots/before swap hook, already cached dynamic fee.snap b/.forge-snapshots/before swap hook, already cached dynamic fee.snap index 1a2ef0b94..3cbc98f73 100644 --- a/.forge-snapshots/before swap hook, already cached dynamic fee.snap +++ b/.forge-snapshots/before swap hook, already cached dynamic fee.snap @@ -1 +1 @@ -185924 \ No newline at end of file +185454 \ No newline at end of file diff --git a/.forge-snapshots/cached dynamic fee, no hooks.snap b/.forge-snapshots/cached dynamic fee, no hooks.snap index 4fabf547a..298079c1f 100644 --- a/.forge-snapshots/cached dynamic fee, no hooks.snap +++ b/.forge-snapshots/cached dynamic fee, no hooks.snap @@ -1 +1 @@ -139924 \ No newline at end of file +139454 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 1 token.snap b/.forge-snapshots/donate gas with 1 token.snap index 8f9327393..2923d0ce4 100644 --- a/.forge-snapshots/donate gas with 1 token.snap +++ b/.forge-snapshots/donate gas with 1 token.snap @@ -1 +1 @@ -132925 \ No newline at end of file +132530 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 2 tokens.snap b/.forge-snapshots/donate gas with 2 tokens.snap index bf60db819..8b6772d67 100644 --- a/.forge-snapshots/donate gas with 2 tokens.snap +++ b/.forge-snapshots/donate gas with 2 tokens.snap @@ -1 +1 @@ -178082 \ No newline at end of file +177634 \ No newline at end of file diff --git a/.forge-snapshots/gas overhead of no-op lock.snap b/.forge-snapshots/gas overhead of no-op lock.snap index ee6f4fd85..55ffc0c6e 100644 --- a/.forge-snapshots/gas overhead of no-op lock.snap +++ b/.forge-snapshots/gas overhead of no-op lock.snap @@ -1 +1 @@ -15077 \ No newline at end of file +14810 \ No newline at end of file diff --git a/.forge-snapshots/initialize.snap b/.forge-snapshots/initialize.snap index 03c28dc28..d9dfed896 100644 --- a/.forge-snapshots/initialize.snap +++ b/.forge-snapshots/initialize.snap @@ -1 +1 @@ -73148 \ No newline at end of file +72764 \ No newline at end of file diff --git a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap index 7c04a5009..2257feb13 100644 --- a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap +++ b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap @@ -1 +1 @@ -247546 \ No newline at end of file +247151 \ No newline at end of file diff --git a/.forge-snapshots/modify position with noop.snap b/.forge-snapshots/modify position with noop.snap index 6ee5340d5..84e389dfe 100644 --- a/.forge-snapshots/modify position with noop.snap +++ b/.forge-snapshots/modify position with noop.snap @@ -1 +1 @@ -54443 \ No newline at end of file +54101 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 70341d9de..3b3195df2 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -24173 \ No newline at end of file +24093 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with empty hook.snap b/.forge-snapshots/removeLiquidity with empty hook.snap index 9297a8602..d2c53fce5 100644 --- a/.forge-snapshots/removeLiquidity with empty hook.snap +++ b/.forge-snapshots/removeLiquidity with empty hook.snap @@ -1 +1 @@ -100428 \ No newline at end of file +99936 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with native token.snap b/.forge-snapshots/removeLiquidity with native token.snap index 61adfa7b6..baf590c91 100644 --- a/.forge-snapshots/removeLiquidity with native token.snap +++ b/.forge-snapshots/removeLiquidity with native token.snap @@ -1 +1 @@ -201801 \ No newline at end of file +201309 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity.snap b/.forge-snapshots/removeLiquidity.snap index f16e747f8..04918ec24 100644 --- a/.forge-snapshots/removeLiquidity.snap +++ b/.forge-snapshots/removeLiquidity.snap @@ -1 +1 @@ -198078 \ No newline at end of file +197586 \ No newline at end of file diff --git a/.forge-snapshots/simple swap with native.snap b/.forge-snapshots/simple swap with native.snap index 583cf05b1..6f27b91e1 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -188497 \ No newline at end of file +188027 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index ca7e4e88f..7ce4b83c1 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -197034 \ No newline at end of file +196564 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapEOAInitiated.snap b/.forge-snapshots/simpleSwapEOAInitiated.snap index 68f27b179..38c1a1eb5 100644 --- a/.forge-snapshots/simpleSwapEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapEOAInitiated.snap @@ -1 +1 @@ -166955 \ No newline at end of file +166450 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap index e0e48d71e..aaea2d8e3 100644 --- a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap @@ -1 +1 @@ -165602 \ No newline at end of file +165132 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity with native token.snap b/.forge-snapshots/swap against liquidity with native token.snap index 11afa3d31..0946afcde 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -119160 \ No newline at end of file +118690 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index c17ca5999..210a6bf3b 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -106624 \ No newline at end of file +106154 \ No newline at end of file diff --git a/.forge-snapshots/swap burn 6909 for input.snap b/.forge-snapshots/swap burn 6909 for input.snap index 9978f4e07..ab4b64fdb 100644 --- a/.forge-snapshots/swap burn 6909 for input.snap +++ b/.forge-snapshots/swap burn 6909 for input.snap @@ -1 +1 @@ -126836 \ No newline at end of file +126344 \ No newline at end of file diff --git a/.forge-snapshots/swap burn native 6909 for input.snap b/.forge-snapshots/swap burn native 6909 for input.snap index cbe5a99ae..2baddebf2 100644 --- a/.forge-snapshots/swap burn native 6909 for input.snap +++ b/.forge-snapshots/swap burn native 6909 for input.snap @@ -1 +1 @@ -122789 \ No newline at end of file +122297 \ No newline at end of file diff --git a/.forge-snapshots/swap mint native output as 6909.snap b/.forge-snapshots/swap mint native output as 6909.snap index 875660dde..f858065c5 100644 --- a/.forge-snapshots/swap mint native output as 6909.snap +++ b/.forge-snapshots/swap mint native output as 6909.snap @@ -1 +1 @@ -191024 \ No newline at end of file +190554 \ No newline at end of file diff --git a/.forge-snapshots/swap mint output as 6909.snap b/.forge-snapshots/swap mint output as 6909.snap index d1a56c5b3..3ad1208f2 100644 --- a/.forge-snapshots/swap mint output as 6909.snap +++ b/.forge-snapshots/swap mint output as 6909.snap @@ -1 +1 @@ -207829 \ No newline at end of file +207359 \ No newline at end of file diff --git a/.forge-snapshots/swap with dynamic fee.snap b/.forge-snapshots/swap with dynamic fee.snap index fb67a27a3..90130ac49 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -185025 \ No newline at end of file +184555 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index 16ae102fc..3803f5a6e 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -106602 \ No newline at end of file +106132 \ No newline at end of file diff --git a/.forge-snapshots/swap with noop.snap b/.forge-snapshots/swap with noop.snap index 90c4e6542..cd37cba40 100644 --- a/.forge-snapshots/swap with noop.snap +++ b/.forge-snapshots/swap with noop.snap @@ -1 +1 @@ -47175 \ No newline at end of file +46833 \ No newline at end of file diff --git a/.forge-snapshots/update dynamic fee in before swap.snap b/.forge-snapshots/update dynamic fee in before swap.snap index f32d421b7..849cdfec2 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -191729 \ No newline at end of file +191259 \ No newline at end of file diff --git a/src/PoolManager.sol b/src/PoolManager.sol index 113980688..1e20b16ff 100644 --- a/src/PoolManager.sol +++ b/src/PoolManager.sol @@ -89,8 +89,8 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { } /// @inheritdoc IPoolManager - function getLock() external view override returns (address locker, address lockCaller) { - return (Locker.getLocker(), Locker.getLockCaller()); + function getLocker() external view override returns (address locker) { + return Locker.getLocker(); } /// @notice This will revert if a function is called by any address other than the current locker OR the most recently called, pre-permissioned hook. @@ -133,13 +133,13 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { // Get the lock caller because thats an EOA and is not user-controlable if (Locker.isLocked()) revert AlreadyLocked(); - Locker.setLockerAndCaller(lockTarget, msg.sender); + Locker.setLocker(lockTarget); // the caller does everything in this callback, including paying what they owe via calls to settle result = ILockCallback(lockTarget).lockAcquired(msg.sender, data); if (NonZeroDeltaCount.read() != 0) revert CurrencyNotSettled(); - Locker.clearLockerAndCaller(); + Locker.clearLocker(); } function _accountDelta(Currency currency, int128 delta) internal { diff --git a/src/interfaces/IPoolManager.sol b/src/interfaces/IPoolManager.sol index f198a8433..40ff5ae72 100644 --- a/src/interfaces/IPoolManager.sol +++ b/src/interfaces/IPoolManager.sol @@ -121,8 +121,8 @@ interface IPoolManager is IFees, IERC6909Claims { /// @notice Returns the reserves for a given ERC20 currency function reservesOf(Currency currency) external view returns (uint256); - /// @notice Returns the locker and lockCaller of the pool - function getLock() external view returns (address locker, address lockCaller); + /// @notice Returns the locker of the pool + function getLocker() external view returns (address locker); /// @notice Returns the number of nonzero deltas open on the PoolManager that must be zerod by the close of the initial lock. function getLockNonzeroDeltaCount() external view returns (uint256 _nonzeroDeltaCount); diff --git a/src/libraries/Locker.sol b/src/libraries/Locker.sol index e0386fb91..2f45772f8 100644 --- a/src/libraries/Locker.sol +++ b/src/libraries/Locker.sol @@ -8,26 +8,26 @@ import {IHooks} from "../interfaces/IHooks.sol"; /// TODO: This library can be deleted when we have the transient keyword support in solidity. library Locker { // The slot holding the locker, transiently, and the lock caller in the next slot - uint256 constant LOCKER_SLOT = uint256(keccak256("Locker")) - 2; - uint256 constant LOCK_CALLER_SLOT = LOCKER_SLOT + 1; + uint256 constant LOCKER_SLOT = uint256(keccak256("Locker")) - 1; - function setLockerAndCaller(address locker, address lockCaller) internal { + /// @notice Thrown when trying to set the lock target as address(0) + /// we use locker==address(0) to signal that the pool is not locked + error InvalidLocker(); + + function setLocker(address locker) internal { + if (locker == address(0)) revert InvalidLocker(); uint256 slot = LOCKER_SLOT; assembly { // set the locker tstore(slot, locker) - - // set the lock caller - tstore(add(slot, 1), lockCaller) } } - function clearLockerAndCaller() internal { + function clearLocker() internal { uint256 slot = LOCKER_SLOT; assembly { tstore(slot, 0) - tstore(add(slot, 1), 0) } } @@ -39,13 +39,6 @@ library Locker { } function isLocked() internal view returns (bool) { - return Locker.getLockCaller() != address(0); - } - - function getLockCaller() internal view returns (address locker) { - uint256 slot = LOCK_CALLER_SLOT; - assembly { - locker := tload(slot) - } + return getLocker() != address(0); } } diff --git a/src/test/PoolNestedActionsTest.sol b/src/test/PoolNestedActionsTest.sol index c96ef1a6d..e792e3e49 100644 --- a/src/test/PoolNestedActionsTest.sol +++ b/src/test/PoolNestedActionsTest.sol @@ -42,16 +42,14 @@ contract PoolNestedActionsTest is Test, ILockCallback { } function _nestedLock() internal { - (address locker, address lockCaller) = manager.getLock(); + address locker = manager.getLocker(); assertEq(locker, address(this)); - assertEq(lockCaller, user); vm.expectRevert(abi.encodeWithSelector(IPoolManager.AlreadyLocked.selector)); manager.lock(address(this), ""); - (locker, lockCaller) = manager.getLock(); + locker = manager.getLocker(); assertEq(locker, address(this)); - assertEq(lockCaller, user); } } @@ -94,22 +92,19 @@ contract NestedActionExecutor is Test, PoolTestBase { } function _nestedLock() internal { - (address locker, address lockCaller) = manager.getLock(); + (address locker) = manager.getLocker(); assertEq(locker, msg.sender); - assertEq(lockCaller, user); vm.expectRevert(abi.encodeWithSelector(IPoolManager.AlreadyLocked.selector)); manager.lock(address(this), ""); - (locker, lockCaller) = manager.getLock(); + (locker) = manager.getLocker(); assertEq(locker, msg.sender); - assertEq(lockCaller, user); } function _swap() internal { - (address locker, address lockCaller) = manager.getLock(); + (address locker) = manager.getLocker(); assertTrue(locker != address(this), "Locker wrong"); - assertEq(lockCaller, user); (,,, int256 deltaLockerBefore0) = _fetchBalances(key.currency0, user, locker); (,,, int256 deltaLockerBefore1) = _fetchBalances(key.currency1, user, locker); @@ -135,9 +130,8 @@ contract NestedActionExecutor is Test, PoolTestBase { } function _addLiquidity() internal { - (address locker, address lockCaller) = manager.getLock(); + address locker = manager.getLocker(); assertTrue(locker != address(this), "Locker wrong"); - assertEq(lockCaller, user); (,,, int256 deltaLockerBefore0) = _fetchBalances(key.currency0, user, locker); (,,, int256 deltaLockerBefore1) = _fetchBalances(key.currency1, user, locker); @@ -162,9 +156,8 @@ contract NestedActionExecutor is Test, PoolTestBase { // cannot remove non-existent liquidity - need to perform an add before this removal function _removeLiquidity() internal { - (address locker, address lockCaller) = manager.getLock(); + address locker = manager.getLocker(); assertTrue(locker != address(this), "Locker wrong"); - assertEq(lockCaller, user); (,,, int256 deltaLockerBefore0) = _fetchBalances(key.currency0, user, locker); (,,, int256 deltaLockerBefore1) = _fetchBalances(key.currency1, user, locker); @@ -188,9 +181,8 @@ contract NestedActionExecutor is Test, PoolTestBase { } function _donate() internal { - (address locker, address lockCaller) = manager.getLock(); + address locker = manager.getLocker(); assertTrue(locker != address(this), "Locker wrong"); - assertEq(lockCaller, user); (,,, int256 deltaLockerBefore0) = _fetchBalances(key.currency0, user, locker); (,,, int256 deltaLockerBefore1) = _fetchBalances(key.currency1, user, locker); diff --git a/test/Locker.t.sol b/test/Locker.t.sol index 69cb23152..1e63909b9 100644 --- a/test/Locker.t.sol +++ b/test/Locker.t.sol @@ -8,42 +8,38 @@ contract LockerTest is Test { address constant ADDRESS_AS = 0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa; address constant ADDRESS_BS = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; - function test_setLockerAndCaller() public { + function test_setLocker(address locker) public { assertEq(address(Locker.getLocker()), address(0)); - assertEq(address(Locker.getLockCaller()), address(0)); - Locker.setLockerAndCaller(ADDRESS_AS, ADDRESS_BS); - - assertEq(address(Locker.getLocker()), ADDRESS_AS); - assertEq(address(Locker.getLockCaller()), ADDRESS_BS); - - // in the way this library is used in V4, this function will never be called when non-0 - Locker.setLockerAndCaller(ADDRESS_BS, ADDRESS_AS); - - assertEq(address(Locker.getLocker()), ADDRESS_BS); - assertEq(address(Locker.getLockCaller()), ADDRESS_AS); + if (locker == address(0)) { + vm.expectRevert(Locker.InvalidLocker.selector); + Locker.setLocker(locker); + } else { + Locker.setLocker(locker); + assertEq(address(Locker.getLocker()), locker); + } } - function test_clearLockerAndCaller() public { - Locker.setLockerAndCaller(ADDRESS_AS, ADDRESS_BS); + function test_clearLocker(address locker) public { + vm.assume(locker != address(0)); + Locker.setLocker(locker); - assertEq(address(Locker.getLocker()), ADDRESS_AS); - assertEq(address(Locker.getLockCaller()), ADDRESS_BS); + assertEq(address(Locker.getLocker()), locker); - Locker.clearLockerAndCaller(); + Locker.clearLocker(); assertEq(address(Locker.getLocker()), address(0)); - assertEq(address(Locker.getLockCaller()), address(0)); } - function test_isLocked() public { + function test_isLocked(address locker) public { + vm.assume(locker != address(0)); assertFalse(Locker.isLocked()); - Locker.setLockerAndCaller(ADDRESS_AS, ADDRESS_BS); + Locker.setLocker(locker); assertTrue(Locker.isLocked()); - Locker.clearLockerAndCaller(); + Locker.clearLocker(); assertFalse(Locker.isLocked()); } From dff0d82d7ccb53b22735b4c1798b60054103938a Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Wed, 14 Feb 2024 11:43:31 -0300 Subject: [PATCH 12/19] merge errors --- .forge-snapshots/poolManager bytecode size.snap | 2 +- .forge-snapshots/simpleSwapEOAInitiated.snap | 2 +- test/utils/SwapHelper.t.sol | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 60ee0a347..09ce280b6 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -24361 +23847 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapEOAInitiated.snap b/.forge-snapshots/simpleSwapEOAInitiated.snap index 414f3217f..38c1a1eb5 100644 --- a/.forge-snapshots/simpleSwapEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapEOAInitiated.snap @@ -1 +1 @@ -173759 +166450 \ No newline at end of file diff --git a/test/utils/SwapHelper.t.sol b/test/utils/SwapHelper.t.sol index 64fd4990c..e89950c72 100644 --- a/test/utils/SwapHelper.t.sol +++ b/test/utils/SwapHelper.t.sol @@ -18,7 +18,6 @@ import {Deployers} from "./Deployers.sol"; import {Fees} from "../../src/Fees.sol"; import {PoolId, PoolIdLibrary} from "../../src/types/PoolId.sol"; import {PoolKey} from "../../src/types/PoolKey.sol"; -import {AccessLockHook} from "../../src/test/AccessLockHook.sol"; import {IERC20Minimal} from "../../src/interfaces/external/IERC20Minimal.sol"; import {BalanceDelta} from "../../src/types/BalanceDelta.sol"; From 47bea1a7035d03256c1371c26e97810f633df795 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Wed, 14 Feb 2024 15:11:04 -0300 Subject: [PATCH 13/19] Flipping the sign of deltas --- .../addLiquidity with empty hook.snap | 2 +- .../addLiquidity with native token.snap | 2 +- .forge-snapshots/addLiquidity.snap | 2 +- ...swap hook, already cached dynamic fee.snap | 2 +- .../cached dynamic fee, no hooks.snap | 2 +- .forge-snapshots/donate gas with 1 token.snap | 2 +- .../donate gas with 2 tokens.snap | 2 +- .../mintWithEmptyHookEOAInitiated.snap | 2 +- .../modify position with noop.snap | 2 +- .../poolManager bytecode size.snap | 2 +- .../removeLiquidity with empty hook.snap | 2 +- .../removeLiquidity with native token.snap | 2 +- .forge-snapshots/removeLiquidity.snap | 2 +- .forge-snapshots/simple swap with native.snap | 2 +- .forge-snapshots/simple swap.snap | 2 +- .forge-snapshots/simpleSwapEOAInitiated.snap | 2 +- .../simpleSwapNativeEOAInitiated.snap | 2 +- ...p against liquidity with native token.snap | 2 +- .forge-snapshots/swap against liquidity.snap | 2 +- .../swap burn 6909 for input.snap | 2 +- .../swap burn native 6909 for input.snap | 2 +- .../swap mint native output as 6909.snap | 2 +- .../swap mint output as 6909.snap | 2 +- .forge-snapshots/swap with dynamic fee.snap | 2 +- .forge-snapshots/swap with hooks.snap | 2 +- .../update dynamic fee in before swap.snap | 2 +- src/PoolManager.sol | 15 +++++------ src/libraries/Pool.sol | 19 +++++++------- src/libraries/SafeCast.sol | 8 ++++++ src/test/PoolClaimsTest.sol | 2 +- src/test/PoolDonateTest.sol | 15 ++++++----- src/test/PoolModifyLiquidityTest.sol | 10 ++++---- src/test/PoolNestedActionsTest.sol | 12 ++++----- src/test/PoolSwapTest.sol | 25 ++++++++++--------- src/test/PoolTakeTest.sol | 6 ++--- src/test/PoolTestBase.sol | 17 +++++++------ test/utils/SwapHelper.t.sol | 22 ++++++++-------- 37 files changed, 107 insertions(+), 96 deletions(-) diff --git a/.forge-snapshots/addLiquidity with empty hook.snap b/.forge-snapshots/addLiquidity with empty hook.snap index cfc6e3e4e..e4a72be6b 100644 --- a/.forge-snapshots/addLiquidity with empty hook.snap +++ b/.forge-snapshots/addLiquidity with empty hook.snap @@ -1 +1 @@ -313034 \ No newline at end of file +313169 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity with native token.snap b/.forge-snapshots/addLiquidity with native token.snap index de7c2ff33..e0597c136 100644 --- a/.forge-snapshots/addLiquidity with native token.snap +++ b/.forge-snapshots/addLiquidity with native token.snap @@ -1 +1 @@ -193189 \ No newline at end of file +193321 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity.snap b/.forge-snapshots/addLiquidity.snap index 1750a6692..e6d46e2df 100644 --- a/.forge-snapshots/addLiquidity.snap +++ b/.forge-snapshots/addLiquidity.snap @@ -1 +1 @@ -193167 \ No newline at end of file +193302 \ No newline at end of file diff --git a/.forge-snapshots/before swap hook, already cached dynamic fee.snap b/.forge-snapshots/before swap hook, already cached dynamic fee.snap index 3cbc98f73..e27ec07df 100644 --- a/.forge-snapshots/before swap hook, already cached dynamic fee.snap +++ b/.forge-snapshots/before swap hook, already cached dynamic fee.snap @@ -1 +1 @@ -185454 \ No newline at end of file +186054 \ No newline at end of file diff --git a/.forge-snapshots/cached dynamic fee, no hooks.snap b/.forge-snapshots/cached dynamic fee, no hooks.snap index 298079c1f..f4e593d3e 100644 --- a/.forge-snapshots/cached dynamic fee, no hooks.snap +++ b/.forge-snapshots/cached dynamic fee, no hooks.snap @@ -1 +1 @@ -139454 \ No newline at end of file +140054 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 1 token.snap b/.forge-snapshots/donate gas with 1 token.snap index 2923d0ce4..55d9dcc74 100644 --- a/.forge-snapshots/donate gas with 1 token.snap +++ b/.forge-snapshots/donate gas with 1 token.snap @@ -1 +1 @@ -132530 \ No newline at end of file +132901 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 2 tokens.snap b/.forge-snapshots/donate gas with 2 tokens.snap index 8b6772d67..be4f7e2ad 100644 --- a/.forge-snapshots/donate gas with 2 tokens.snap +++ b/.forge-snapshots/donate gas with 2 tokens.snap @@ -1 +1 @@ -177634 \ No newline at end of file +178078 \ No newline at end of file diff --git a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap index 2257feb13..1e7b8a0ec 100644 --- a/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap +++ b/.forge-snapshots/mintWithEmptyHookEOAInitiated.snap @@ -1 +1 @@ -247151 \ No newline at end of file +247213 \ No newline at end of file diff --git a/.forge-snapshots/modify position with noop.snap b/.forge-snapshots/modify position with noop.snap index 84e389dfe..1af8f8182 100644 --- a/.forge-snapshots/modify position with noop.snap +++ b/.forge-snapshots/modify position with noop.snap @@ -1 +1 @@ -54101 \ No newline at end of file +54090 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 09ce280b6..9c93ec591 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -23847 \ No newline at end of file +23925 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with empty hook.snap b/.forge-snapshots/removeLiquidity with empty hook.snap index d2c53fce5..b18073dc9 100644 --- a/.forge-snapshots/removeLiquidity with empty hook.snap +++ b/.forge-snapshots/removeLiquidity with empty hook.snap @@ -1 +1 @@ -99936 \ No newline at end of file +99937 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with native token.snap b/.forge-snapshots/removeLiquidity with native token.snap index baf590c91..e8fbdf759 100644 --- a/.forge-snapshots/removeLiquidity with native token.snap +++ b/.forge-snapshots/removeLiquidity with native token.snap @@ -1 +1 @@ -201309 \ No newline at end of file +201310 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity.snap b/.forge-snapshots/removeLiquidity.snap index 04918ec24..1737edf65 100644 --- a/.forge-snapshots/removeLiquidity.snap +++ b/.forge-snapshots/removeLiquidity.snap @@ -1 +1 @@ -197586 \ No newline at end of file +197587 \ No newline at end of file diff --git a/.forge-snapshots/simple swap with native.snap b/.forge-snapshots/simple swap with native.snap index 6f27b91e1..f2b3e68b2 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -188027 \ No newline at end of file +188624 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index 7ce4b83c1..a7b4db43b 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -196564 \ No newline at end of file +197164 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapEOAInitiated.snap b/.forge-snapshots/simpleSwapEOAInitiated.snap index 38c1a1eb5..a856fb2a7 100644 --- a/.forge-snapshots/simpleSwapEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapEOAInitiated.snap @@ -1 +1 @@ -166450 \ No newline at end of file +167083 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap index aaea2d8e3..a7dbfb203 100644 --- a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap @@ -1 +1 @@ -165132 \ No newline at end of file +165611 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity with native token.snap b/.forge-snapshots/swap against liquidity with native token.snap index 0946afcde..504253c22 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -118690 \ No newline at end of file +119287 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index 210a6bf3b..399e9fac5 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -106154 \ No newline at end of file +106754 \ No newline at end of file diff --git a/.forge-snapshots/swap burn 6909 for input.snap b/.forge-snapshots/swap burn 6909 for input.snap index ab4b64fdb..cdddfb5d1 100644 --- a/.forge-snapshots/swap burn 6909 for input.snap +++ b/.forge-snapshots/swap burn 6909 for input.snap @@ -1 +1 @@ -126344 \ No newline at end of file +126911 \ No newline at end of file diff --git a/.forge-snapshots/swap burn native 6909 for input.snap b/.forge-snapshots/swap burn native 6909 for input.snap index 2baddebf2..2b77700ea 100644 --- a/.forge-snapshots/swap burn native 6909 for input.snap +++ b/.forge-snapshots/swap burn native 6909 for input.snap @@ -1 +1 @@ -122297 \ No newline at end of file +122864 \ No newline at end of file diff --git a/.forge-snapshots/swap mint native output as 6909.snap b/.forge-snapshots/swap mint native output as 6909.snap index f858065c5..10ae205d0 100644 --- a/.forge-snapshots/swap mint native output as 6909.snap +++ b/.forge-snapshots/swap mint native output as 6909.snap @@ -1 +1 @@ -190554 \ No newline at end of file +191187 \ No newline at end of file diff --git a/.forge-snapshots/swap mint output as 6909.snap b/.forge-snapshots/swap mint output as 6909.snap index 3ad1208f2..d5a69193a 100644 --- a/.forge-snapshots/swap mint output as 6909.snap +++ b/.forge-snapshots/swap mint output as 6909.snap @@ -1 +1 @@ -207359 \ No newline at end of file +207992 \ No newline at end of file diff --git a/.forge-snapshots/swap with dynamic fee.snap b/.forge-snapshots/swap with dynamic fee.snap index 90130ac49..7ee2664e2 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -184555 \ No newline at end of file +185155 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index 3803f5a6e..26c53291b 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -106132 \ No newline at end of file +106732 \ No newline at end of file diff --git a/.forge-snapshots/update dynamic fee in before swap.snap b/.forge-snapshots/update dynamic fee in before swap.snap index 849cdfec2..bf8a7f23d 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -191259 \ No newline at end of file +191859 \ No newline at end of file diff --git a/src/PoolManager.sol b/src/PoolManager.sol index 21b2fab1e..8c32dd114 100644 --- a/src/PoolManager.sol +++ b/src/PoolManager.sol @@ -227,8 +227,8 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { ); _accountPoolBalanceDelta(key, delta); - // the fee is on the input currency + // the fee is on the input currency unchecked { if (feeForProtocol > 0) { protocolFeesAccrued[params.zeroForOne ? key.currency0 : key.currency1] += feeForProtocol; @@ -236,7 +236,7 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { } emit Swap( - id, msg.sender, delta.amount0(), delta.amount1(), state.sqrtPriceX96, state.liquidity, state.tick, swapFee + id, msg.sender, -delta.amount0(), -delta.amount1(), state.sqrtPriceX96, state.liquidity, state.tick, swapFee ); key.hooks.afterSwap(key, params, delta, hookData); @@ -266,7 +266,8 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { /// @inheritdoc IPoolManager function take(Currency currency, address to, uint256 amount) external override noDelegateCall isLocked { - _accountDelta(currency, amount.toInt128()); + // flipping a postive number in an int is always safe + _accountDelta(currency, -(amount.toInt128())); reservesOf[currency] -= amount; currency.transfer(to, amount); } @@ -276,19 +277,19 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { uint256 reservesBefore = reservesOf[currency]; reservesOf[currency] = currency.balanceOfSelf(); paid = reservesOf[currency] - reservesBefore; - // subtraction must be safe - _accountDelta(currency, -(paid.toInt128())); + _accountDelta(currency, paid.toInt128()); } /// @inheritdoc IPoolManager function mint(address to, uint256 id, uint256 amount) external override noDelegateCall isLocked { - _accountDelta(CurrencyLibrary.fromId(id), amount.toInt128()); + // flipping a postive number in an int is always safe + _accountDelta(CurrencyLibrary.fromId(id), -(amount.toInt128())); _mint(to, id, amount); } /// @inheritdoc IPoolManager function burn(address from, uint256 id, uint256 amount) external override noDelegateCall isLocked { - _accountDelta(CurrencyLibrary.fromId(id), -(amount.toInt128())); + _accountDelta(CurrencyLibrary.fromId(id), amount.toInt128()); _burnFrom(from, id, amount); } diff --git a/src/libraries/Pool.sol b/src/libraries/Pool.sol index 89b437373..a0869394c 100644 --- a/src/libraries/Pool.sol +++ b/src/libraries/Pool.sol @@ -210,7 +210,7 @@ library Pool { // current tick is below the passed range; liquidity can only become in range by crossing from left to // right, when we'll need _more_ currency0 (it's becoming more valuable) so user must provide it result = result - + toBalanceDelta( + - toBalanceDelta( SqrtPriceMath.getAmount0Delta( TickMath.getSqrtRatioAtTick(params.tickLower), TickMath.getSqrtRatioAtTick(params.tickUpper), @@ -220,7 +220,7 @@ library Pool { ); } else if (self.slot0.tick < params.tickUpper) { result = result - + toBalanceDelta( + - toBalanceDelta( SqrtPriceMath.getAmount0Delta( self.slot0.sqrtPriceX96, TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta ).toInt128(), @@ -236,7 +236,7 @@ library Pool { // current tick is above the passed range; liquidity can only become in range by crossing from right to // left, when we'll need _more_ currency1 (it's becoming more valuable) so user must provide it result = result - + toBalanceDelta( + - toBalanceDelta( 0, SqrtPriceMath.getAmount1Delta( TickMath.getSqrtRatioAtTick(params.tickLower), @@ -248,7 +248,7 @@ library Pool { } // Fees earned from LPing are removed from the pool balance. - result = result - toBalanceDelta(feesOwed0.toInt128(), feesOwed1.toInt128()); + result = result + toBalanceDelta(feesOwed0.toInt128(), feesOwed1.toInt128()); } struct SwapCache { @@ -444,16 +444,17 @@ library Pool { self.feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; } + // in an unchecked block so sign flipping is not safe without a helper function unchecked { if (params.zeroForOne == exactInput) { result = toBalanceDelta( - (params.amountSpecified - state.amountSpecifiedRemaining).toInt128(), - state.amountCalculated.toInt128() + (params.amountSpecified - state.amountSpecifiedRemaining).toInt128().flipSign(), + state.amountCalculated.toInt128().flipSign() ); } else { result = toBalanceDelta( - state.amountCalculated.toInt128(), - (params.amountSpecified - state.amountSpecifiedRemaining).toInt128() + state.amountCalculated.toInt128().flipSign(), + (params.amountSpecified - state.amountSpecifiedRemaining).toInt128().flipSign() ); } } @@ -462,7 +463,7 @@ library Pool { /// @notice Donates the given amount of currency0 and currency1 to the pool function donate(State storage state, uint256 amount0, uint256 amount1) internal returns (BalanceDelta delta) { if (state.liquidity == 0) revert NoLiquidityToReceiveFees(); - delta = toBalanceDelta(amount0.toInt128(), amount1.toInt128()); + delta = toBalanceDelta(-(amount0.toInt128()), -(amount1.toInt128())); unchecked { if (amount0 > 0) { state.feeGrowthGlobal0X128 += FullMath.mulDiv(amount0, FixedPoint128.Q128, state.liquidity); diff --git a/src/libraries/SafeCast.sol b/src/libraries/SafeCast.sol index 5eb5bd474..82bcac38c 100644 --- a/src/libraries/SafeCast.sol +++ b/src/libraries/SafeCast.sol @@ -37,4 +37,12 @@ library SafeCast { if (y > uint128(type(int128).max)) revert SafeCastOverflow(); z = int128(int256(y)); } + + /// @notice Flip the sign of an int128, revert on overflow + /// @param y The int128 to flip the sign of + /// @return The flipped integer, still an int128 + function flipSign(int128 y) internal pure returns (int128) { + // this is not unchecked, so it will revert if y==type(int128).min + return -y; + } } diff --git a/src/test/PoolClaimsTest.sol b/src/test/PoolClaimsTest.sol index cf42041a6..f934615b4 100644 --- a/src/test/PoolClaimsTest.sol +++ b/src/test/PoolClaimsTest.sol @@ -40,7 +40,7 @@ contract PoolClaimsTest is PoolTestBase { if (data.deposit) { manager.mint(data.user, data.currency.toId(), uint128(data.amount)); - _settle(data.currency, data.user, data.amount.toInt128(), true); + _settle(data.currency, data.user, -data.amount.toInt128(), true); } else { manager.burn(data.user, data.currency.toId(), uint128(data.amount)); _take(data.currency, data.user, data.amount.toInt128(), true); diff --git a/src/test/PoolDonateTest.sol b/src/test/PoolDonateTest.sol index 7ee393db2..eefb2cf8d 100644 --- a/src/test/PoolDonateTest.sol +++ b/src/test/PoolDonateTest.sol @@ -6,11 +6,10 @@ import {IPoolManager} from "../interfaces/IPoolManager.sol"; import {PoolKey} from "../types/PoolKey.sol"; import {BalanceDelta, BalanceDeltaLibrary} from "../types/BalanceDelta.sol"; import {PoolTestBase} from "./PoolTestBase.sol"; -import {Test} from "forge-std/Test.sol"; import {IHooks} from "../interfaces/IHooks.sol"; import {Hooks} from "../libraries/Hooks.sol"; -contract PoolDonateTest is PoolTestBase, Test { +contract PoolDonateTest is PoolTestBase { using CurrencyLibrary for Currency; using Hooks for IHooks; @@ -61,8 +60,8 @@ contract PoolDonateTest is PoolTestBase, Test { assertEq(reserveBefore0, reserveAfter0); assertEq(reserveBefore1, reserveAfter1); if (!data.key.hooks.hasPermission(Hooks.NO_OP_FLAG)) { - assertEq(deltaAfter0, int256(data.amount0)); - assertEq(deltaAfter1, int256(data.amount1)); + assertEq(deltaAfter0, -int256(data.amount0)); + assertEq(deltaAfter1, -int256(data.amount1)); } if (delta == BalanceDeltaLibrary.MAXIMUM_DELTA) { @@ -71,10 +70,10 @@ contract PoolDonateTest is PoolTestBase, Test { return abi.encode(delta); } - if (deltaAfter0 > 0) _settle(data.key.currency0, data.sender, int128(deltaAfter0), true); - if (deltaAfter1 > 0) _settle(data.key.currency1, data.sender, int128(deltaAfter1), true); - if (deltaAfter0 < 0) _take(data.key.currency0, data.sender, int128(deltaAfter0), true); - if (deltaAfter1 < 0) _take(data.key.currency1, data.sender, int128(deltaAfter1), true); + if (deltaAfter0 < 0) _settle(data.key.currency0, data.sender, int128(deltaAfter0), true); + if (deltaAfter1 < 0) _settle(data.key.currency1, data.sender, int128(deltaAfter1), true); + if (deltaAfter0 > 0) _take(data.key.currency0, data.sender, int128(deltaAfter0), true); + if (deltaAfter1 > 0) _take(data.key.currency1, data.sender, int128(deltaAfter1), true); return abi.encode(delta); } diff --git a/src/test/PoolModifyLiquidityTest.sol b/src/test/PoolModifyLiquidityTest.sol index 3cf7a924f..255fd1657 100644 --- a/src/test/PoolModifyLiquidityTest.sol +++ b/src/test/PoolModifyLiquidityTest.sol @@ -66,7 +66,7 @@ contract PoolModifyLiquidityTest is Test, PoolTestBase { (,,, int256 delta0) = _fetchBalances(data.key.currency0, data.sender, address(this)); (,,, int256 delta1) = _fetchBalances(data.key.currency1, data.sender, address(this)); - if (data.params.liquidityDelta > 0) { + if (data.params.liquidityDelta < 0) { assert(delta0 > 0 || delta1 > 0 || data.key.hooks.hasPermission(Hooks.NO_OP_FLAG)); assert(!(delta0 < 0 || delta1 < 0)); } else { @@ -74,10 +74,10 @@ contract PoolModifyLiquidityTest is Test, PoolTestBase { assert(!(delta0 > 0 || delta1 > 0)); } - if (delta0 > 0) _settle(data.key.currency0, data.sender, int128(delta0), data.settleUsingTransfer); - if (delta1 > 0) _settle(data.key.currency1, data.sender, int128(delta1), data.settleUsingTransfer); - if (delta0 < 0) _take(data.key.currency0, data.sender, int128(delta0), data.withdrawTokens); - if (delta1 < 0) _take(data.key.currency1, data.sender, int128(delta1), data.withdrawTokens); + if (delta0 < 0) _settle(data.key.currency0, data.sender, int128(delta0), data.settleUsingTransfer); + if (delta1 < 0) _settle(data.key.currency1, data.sender, int128(delta1), data.settleUsingTransfer); + if (delta0 > 0) _take(data.key.currency0, data.sender, int128(delta0), data.withdrawTokens); + if (delta1 > 0) _take(data.key.currency1, data.sender, int128(delta1), data.withdrawTokens); return abi.encode(delta); } diff --git a/src/test/PoolNestedActionsTest.sol b/src/test/PoolNestedActionsTest.sol index e792e3e49..76548e4cb 100644 --- a/src/test/PoolNestedActionsTest.sol +++ b/src/test/PoolNestedActionsTest.sol @@ -120,8 +120,8 @@ contract NestedActionExecutor is Test, PoolTestBase { assertEq(deltaLockerBefore0, deltaLockerAfter0, "Locker delta 0"); assertEq(deltaLockerBefore1, deltaLockerAfter1, "Locker delta 1"); - assertEq(deltaThisBefore0 + SWAP_PARAMS.amountSpecified, deltaThisAfter0, "Executor delta 0"); - assertEq(deltaThisBefore1 - 98, deltaThisAfter1, "Executor delta 1"); + assertEq(deltaThisBefore0 - SWAP_PARAMS.amountSpecified, deltaThisAfter0, "Executor delta 0"); + assertEq(deltaThisBefore1 + 98, deltaThisAfter1, "Executor delta 1"); assertEq(delta.amount0(), deltaThisAfter0, "Swap delta 0"); assertEq(delta.amount1(), deltaThisAfter1, "Swap delta 1"); @@ -198,10 +198,10 @@ contract NestedActionExecutor is Test, PoolTestBase { assertEq(deltaLockerBefore0, deltaLockerAfter0, "Locker delta 0"); assertEq(deltaLockerBefore1, deltaLockerAfter1, "Locker delta 1"); - assertEq(deltaThisBefore0 + int256(DONATE_AMOUNT0), deltaThisAfter0, "Executor delta 0"); - assertEq(deltaThisBefore1 + int256(DONATE_AMOUNT1), deltaThisAfter1, "Executor delta 1"); - assertEq(delta.amount0(), int256(DONATE_AMOUNT0), "Donate delta 0"); - assertEq(delta.amount1(), int256(DONATE_AMOUNT1), "Donate delta 1"); + assertEq(deltaThisBefore0 - int256(DONATE_AMOUNT0), deltaThisAfter0, "Executor delta 0"); + assertEq(deltaThisBefore1 - int256(DONATE_AMOUNT1), deltaThisAfter1, "Executor delta 1"); + assertEq(-delta.amount0(), int256(DONATE_AMOUNT0), "Donate delta 0"); + assertEq(-delta.amount1(), int256(DONATE_AMOUNT1), "Donate delta 1"); _settle(key.currency0, user, int128(deltaThisAfter0), true); _settle(key.currency1, user, int128(deltaThisAfter1), true); diff --git a/src/test/PoolSwapTest.sol b/src/test/PoolSwapTest.sol index 5cde518f6..58d7c14b9 100644 --- a/src/test/PoolSwapTest.sol +++ b/src/test/PoolSwapTest.sol @@ -9,6 +9,7 @@ import {IHooks} from "../interfaces/IHooks.sol"; import {Hooks} from "../libraries/Hooks.sol"; import {PoolTestBase} from "./PoolTestBase.sol"; import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; import {Hooks} from "../libraries/Hooks.sol"; import {IHooks} from "../interfaces/IHooks.sol"; @@ -74,22 +75,22 @@ contract PoolSwapTest is Test, PoolTestBase { if (data.params.zeroForOne) { if (data.params.amountSpecified > 0) { // exact input, 0 for 1 - assertEq(deltaAfter0, data.params.amountSpecified); - assert(deltaAfter1 < 0); + assertEq(-deltaAfter0, data.params.amountSpecified); + assertGt(deltaAfter1, 0); } else { // exact output, 0 for 1 - assert(deltaAfter0 > 0); - assertEq(deltaAfter1, data.params.amountSpecified); + assertLt(deltaAfter0, 0); + assertEq(-deltaAfter1, data.params.amountSpecified); } } else { if (data.params.amountSpecified > 0) { // exact input, 1 for 0 - assertEq(deltaAfter1, data.params.amountSpecified); - assert(deltaAfter0 < 0); + assertEq(-deltaAfter1, data.params.amountSpecified); + assertGt(deltaAfter0, 0); } else { // exact output, 1 for 0 - assert(deltaAfter1 > 0); - assertEq(deltaAfter0, data.params.amountSpecified); + assertLt(deltaAfter1, 0); + assertEq(-deltaAfter0, data.params.amountSpecified); } } } @@ -100,24 +101,24 @@ contract PoolSwapTest is Test, PoolTestBase { return abi.encode(delta); } - if (deltaAfter0 > 0) { + if (deltaAfter0 < 0) { if (data.testSettings.currencyAlreadySent) { manager.settle(data.key.currency0); } else { _settle(data.key.currency0, data.sender, int128(deltaAfter0), data.testSettings.settleUsingTransfer); } } - if (deltaAfter1 > 0) { + if (deltaAfter1 < 0) { if (data.testSettings.currencyAlreadySent) { manager.settle(data.key.currency1); } else { _settle(data.key.currency1, data.sender, int128(deltaAfter1), data.testSettings.settleUsingTransfer); } } - if (deltaAfter0 < 0) { + if (deltaAfter0 > 0) { _take(data.key.currency0, data.sender, int128(deltaAfter0), data.testSettings.withdrawTokens); } - if (deltaAfter1 < 0) { + if (deltaAfter1 > 0) { _take(data.key.currency1, data.sender, int128(deltaAfter1), data.testSettings.withdrawTokens); } diff --git a/src/test/PoolTakeTest.sol b/src/test/PoolTakeTest.sol index 5a1cfdf9b..66f07b653 100644 --- a/src/test/PoolTakeTest.sol +++ b/src/test/PoolTakeTest.sol @@ -41,17 +41,17 @@ contract PoolTakeTest is Test, PoolTestBase { _fetchBalances(currency, sender, address(this)); assertEq(deltaBefore, 0); - _take(currency, sender, -(amount.toInt128()), true); + _take(currency, sender, amount.toInt128(), true); (uint256 userBalAfter, uint256 pmBalAfter, uint256 reserveAfter, int256 deltaAfter) = _fetchBalances(currency, sender, address(this)); - assertEq(deltaAfter, amount.toInt128()); + assertEq(deltaAfter, -amount.toInt128()); assertEq(userBalAfter - userBalBefore, amount); assertEq(pmBalBefore - pmBalAfter, amount); assertEq(reserveBefore - reserveAfter, amount); assertEq(reserveBefore - reserveAfter, amount); - _settle(currency, sender, amount.toInt128(), true); + _settle(currency, sender, -amount.toInt128(), true); } } diff --git a/src/test/PoolTestBase.sol b/src/test/PoolTestBase.sol index 020934ba6..102fde356 100644 --- a/src/test/PoolTestBase.sol +++ b/src/test/PoolTestBase.sol @@ -1,13 +1,14 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.24; +import {Test} from "forge-std/Test.sol"; import {CurrencyLibrary, Currency} from "../types/Currency.sol"; import {IERC20Minimal} from "../interfaces/external/IERC20Minimal.sol"; import {ILockCallback} from "../interfaces/callback/ILockCallback.sol"; import {IPoolManager} from "../interfaces/IPoolManager.sol"; -abstract contract PoolTestBase is ILockCallback { +abstract contract PoolTestBase is Test, ILockCallback { using CurrencyLibrary for Currency; IPoolManager public immutable manager; @@ -17,25 +18,25 @@ abstract contract PoolTestBase is ILockCallback { } function _take(Currency currency, address recipient, int128 amount, bool withdrawTokens) internal { - assert(amount < 0); + assertGt(amount, 0); if (withdrawTokens) { - manager.take(currency, recipient, uint128(-amount)); + manager.take(currency, recipient, uint128(amount)); } else { - manager.mint(recipient, currency.toId(), uint128(-amount)); + manager.mint(recipient, currency.toId(), uint128(amount)); } } function _settle(Currency currency, address payer, int128 amount, bool settleUsingTransfer) internal { - assert(amount > 0); + assertLt(amount, 0); if (settleUsingTransfer) { if (currency.isNative()) { - manager.settle{value: uint128(amount)}(currency); + manager.settle{value: uint128(-amount)}(currency); } else { - IERC20Minimal(Currency.unwrap(currency)).transferFrom(payer, address(manager), uint128(amount)); + IERC20Minimal(Currency.unwrap(currency)).transferFrom(payer, address(manager), uint128(-amount)); manager.settle(currency); } } else { - manager.burn(payer, currency.toId(), uint128(amount)); + manager.burn(payer, currency.toId(), uint128(-amount)); } } diff --git a/test/utils/SwapHelper.t.sol b/test/utils/SwapHelper.t.sol index e89950c72..b26886ae3 100644 --- a/test/utils/SwapHelper.t.sol +++ b/test/utils/SwapHelper.t.sol @@ -41,31 +41,31 @@ contract SwapHelperTest is Test, Deployers, GasSnapshot { function test_swap_helper_zeroForOne_exactInput() public { int256 amountSpecified = 100; BalanceDelta result = swap(key, true, amountSpecified, ZERO_BYTES); - assertEq(int256(result.amount0()), amountSpecified); + assertEq(-int256(result.amount0()), amountSpecified); } function test_swap_helper_zeroForOne_exactOutput() public { int256 amountSpecified = -100; BalanceDelta result = swap(key, true, amountSpecified, ZERO_BYTES); - assertEq(int256(result.amount1()), amountSpecified); + assertEq(-int256(result.amount1()), amountSpecified); } function test_swap_helper_oneForZero_exactInput() public { int256 amountSpecified = 100; BalanceDelta result = swap(key, false, amountSpecified, ZERO_BYTES); - assertEq(int256(result.amount1()), amountSpecified); + assertEq(-int256(result.amount1()), amountSpecified); } function test_swap_helper_oneForZero_exactOutput() public { int256 amountSpecified = -100; BalanceDelta result = swap(key, false, amountSpecified, ZERO_BYTES); - assertEq(int256(result.amount0()), amountSpecified); + assertEq(-int256(result.amount0()), amountSpecified); } function test_swap_helper_native_zeroForOne_exactInput() public { int256 amountSpecified = 100; BalanceDelta result = swap(nativeKey, true, amountSpecified, ZERO_BYTES); - assertEq(int256(result.amount0()), amountSpecified); + assertEq(-int256(result.amount0()), amountSpecified); } function test_swap_helper_native_zeroForOne_exactOutput() public { @@ -77,38 +77,38 @@ contract SwapHelperTest is Test, Deployers, GasSnapshot { function test_swap_helper_native_oneForZero_exactInput() public { int256 amountSpecified = 100; BalanceDelta result = swap(nativeKey, false, amountSpecified, ZERO_BYTES); - assertEq(int256(result.amount1()), amountSpecified); + assertEq(-int256(result.amount1()), amountSpecified); } function test_swap_helper_native_oneForZero_exactOutput() public { int256 amountSpecified = -100; BalanceDelta result = swap(nativeKey, false, amountSpecified, ZERO_BYTES); - assertEq(int256(result.amount0()), amountSpecified); + assertEq(-int256(result.amount0()), amountSpecified); } // --- Deployers.swapNativeInput() tests --- // function test_swapNativeInput_helper_zeroForOne_exactInput() public { int256 amountSpecified = 100; BalanceDelta result = swapNativeInput(nativeKey, true, amountSpecified, ZERO_BYTES, 100 wei); - assertEq(int256(result.amount0()), amountSpecified); + assertEq(-int256(result.amount0()), amountSpecified); } function test_swapNativeInput_helper_zeroForOne_exactOutput() public { int256 amountSpecified = -100; BalanceDelta result = swapNativeInput(nativeKey, true, amountSpecified, ZERO_BYTES, 200 wei); // overpay - assertEq(int256(result.amount1()), amountSpecified); + assertEq(-int256(result.amount1()), amountSpecified); } function test_swapNativeInput_helper_oneForZero_exactInput() public { int256 amountSpecified = 100; BalanceDelta result = swapNativeInput(nativeKey, false, amountSpecified, ZERO_BYTES, 0 wei); - assertEq(int256(result.amount1()), amountSpecified); + assertEq(-int256(result.amount1()), amountSpecified); } function test_swapNativeInput_helper_oneForZero_exactOutput() public { int256 amountSpecified = -100; BalanceDelta result = swapNativeInput(nativeKey, false, amountSpecified, ZERO_BYTES, 0 wei); - assertEq(int256(result.amount0()), amountSpecified); + assertEq(-int256(result.amount0()), amountSpecified); } function test_swapNativeInput_helper_nonnative_zeroForOne_exactInput() public { From ba1494d5e979276029dea6d84dab77cfeb602615 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Wed, 14 Feb 2024 15:15:42 -0300 Subject: [PATCH 14/19] update comments --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6019eea6..8368925db 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ A more detailed description of Uniswap v4 Core can be found in the draft of the - `settle` - `mint` -Only the net balances owed to the pool (positive) or to the user (negative) are tracked throughout the duration of a lock. This is the `delta` field held in the lock state. Any number of actions can be run on the pools, as long as the deltas accumulated during the lock reach 0 by the lock’s release. This lock and call style architecture gives callers maximum flexibility in integrating with the core code. +Only the net balances owed to the user (positive) or to the pool (negative) are tracked throughout the duration of a lock. This is the `delta` field held in the lock state. Any number of actions can be run on the pools, as long as the deltas accumulated during the lock reach 0 by the lock’s release. This lock and call style architecture gives callers maximum flexibility in integrating with the core code. Additionally, a pool may be initialized with a hook contract, that can implement any of the following callbacks in the lifecycle of pool actions: From d8ebb60ec3cf1677116b88f435c5e5068e57dab4 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Wed, 6 Mar 2024 12:42:41 -0300 Subject: [PATCH 15/19] remove unnecessary read of result --- .../addLiquidity with empty hook.snap | 2 +- .../addLiquidity with native token.snap | 2 +- .forge-snapshots/addLiquidity.snap | 2 +- .../poolManager bytecode size.snap | 2 +- .../removeLiquidity with empty hook.snap | 2 +- .../removeLiquidity with native token.snap | 2 +- .forge-snapshots/removeLiquidity.snap | 2 +- src/libraries/Pool.sol | 53 +++++++++---------- 8 files changed, 32 insertions(+), 35 deletions(-) diff --git a/.forge-snapshots/addLiquidity with empty hook.snap b/.forge-snapshots/addLiquidity with empty hook.snap index 1c96f61c7..7d693797f 100644 --- a/.forge-snapshots/addLiquidity with empty hook.snap +++ b/.forge-snapshots/addLiquidity with empty hook.snap @@ -1 +1 @@ -312838 \ No newline at end of file +312630 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity with native token.snap b/.forge-snapshots/addLiquidity with native token.snap index 511a946be..c54c69058 100644 --- a/.forge-snapshots/addLiquidity with native token.snap +++ b/.forge-snapshots/addLiquidity with native token.snap @@ -1 +1 @@ -193017 \ No newline at end of file +192809 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity.snap b/.forge-snapshots/addLiquidity.snap index 30e2b0c4c..020c828a7 100644 --- a/.forge-snapshots/addLiquidity.snap +++ b/.forge-snapshots/addLiquidity.snap @@ -1 +1 @@ -192998 \ No newline at end of file +192790 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 2d32836cb..dab2b10ac 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -23459 \ No newline at end of file +23414 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with empty hook.snap b/.forge-snapshots/removeLiquidity with empty hook.snap index 45d2eb688..99601b1a8 100644 --- a/.forge-snapshots/removeLiquidity with empty hook.snap +++ b/.forge-snapshots/removeLiquidity with empty hook.snap @@ -1 +1 @@ -99591 \ No newline at end of file +99383 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with native token.snap b/.forge-snapshots/removeLiquidity with native token.snap index de06d078a..9cb6f5adf 100644 --- a/.forge-snapshots/removeLiquidity with native token.snap +++ b/.forge-snapshots/removeLiquidity with native token.snap @@ -1 +1 @@ -200964 \ No newline at end of file +200756 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity.snap b/.forge-snapshots/removeLiquidity.snap index 4cbde64b2..c09e30a7e 100644 --- a/.forge-snapshots/removeLiquidity.snap +++ b/.forge-snapshots/removeLiquidity.snap @@ -1 +1 @@ -197241 \ No newline at end of file +197033 \ No newline at end of file diff --git a/src/libraries/Pool.sol b/src/libraries/Pool.sol index a0869394c..212b79335 100644 --- a/src/libraries/Pool.sol +++ b/src/libraries/Pool.sol @@ -209,25 +209,23 @@ library Pool { if (self.slot0.tick < params.tickLower) { // current tick is below the passed range; liquidity can only become in range by crossing from left to // right, when we'll need _more_ currency0 (it's becoming more valuable) so user must provide it - result = result - - toBalanceDelta( - SqrtPriceMath.getAmount0Delta( - TickMath.getSqrtRatioAtTick(params.tickLower), - TickMath.getSqrtRatioAtTick(params.tickUpper), - params.liquidityDelta - ).toInt128(), - 0 - ); + result = toBalanceDelta( + -SqrtPriceMath.getAmount0Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ).toInt128(), + 0 + ); } else if (self.slot0.tick < params.tickUpper) { - result = result - - toBalanceDelta( - SqrtPriceMath.getAmount0Delta( - self.slot0.sqrtPriceX96, TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta - ).toInt128(), - SqrtPriceMath.getAmount1Delta( - TickMath.getSqrtRatioAtTick(params.tickLower), self.slot0.sqrtPriceX96, params.liquidityDelta - ).toInt128() - ); + result = toBalanceDelta( + -SqrtPriceMath.getAmount0Delta( + self.slot0.sqrtPriceX96, TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta + ).toInt128(), + -SqrtPriceMath.getAmount1Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), self.slot0.sqrtPriceX96, params.liquidityDelta + ).toInt128() + ); self.liquidity = params.liquidityDelta < 0 ? self.liquidity - uint128(-params.liquidityDelta) @@ -235,19 +233,18 @@ library Pool { } else { // current tick is above the passed range; liquidity can only become in range by crossing from right to // left, when we'll need _more_ currency1 (it's becoming more valuable) so user must provide it - result = result - - toBalanceDelta( - 0, - SqrtPriceMath.getAmount1Delta( - TickMath.getSqrtRatioAtTick(params.tickLower), - TickMath.getSqrtRatioAtTick(params.tickUpper), - params.liquidityDelta - ).toInt128() - ); + result = toBalanceDelta( + 0, + -SqrtPriceMath.getAmount1Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ).toInt128() + ); } } - // Fees earned from LPing are removed from the pool balance. + // Fees earned from LPing are added to the user's currency delta. result = result + toBalanceDelta(feesOwed0.toInt128(), feesOwed1.toInt128()); } From f45c75a4384a427ed3d71597f15061e9f53bb936 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Wed, 6 Mar 2024 14:40:37 -0300 Subject: [PATCH 16/19] move delta flip to sqrtprice lib --- .forge-snapshots/addLiquidity with empty hook.snap | 2 +- .forge-snapshots/addLiquidity with native token.snap | 2 +- .forge-snapshots/addLiquidity.snap | 2 +- .forge-snapshots/poolManager bytecode size.snap | 2 +- .forge-snapshots/removeLiquidity with empty hook.snap | 2 +- .../removeLiquidity with native token.snap | 2 +- .forge-snapshots/removeLiquidity.snap | 2 +- src/libraries/Pool.sol | 8 ++++---- src/libraries/SqrtPriceMath.sol | 8 ++++---- test/SqrtPriceMath.t.sol | 10 ++++++++-- 10 files changed, 23 insertions(+), 17 deletions(-) diff --git a/.forge-snapshots/addLiquidity with empty hook.snap b/.forge-snapshots/addLiquidity with empty hook.snap index 7d693797f..fc770a713 100644 --- a/.forge-snapshots/addLiquidity with empty hook.snap +++ b/.forge-snapshots/addLiquidity with empty hook.snap @@ -1 +1 @@ -312630 \ No newline at end of file +312478 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity with native token.snap b/.forge-snapshots/addLiquidity with native token.snap index c54c69058..470796a74 100644 --- a/.forge-snapshots/addLiquidity with native token.snap +++ b/.forge-snapshots/addLiquidity with native token.snap @@ -1 +1 @@ -192809 \ No newline at end of file +192657 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity.snap b/.forge-snapshots/addLiquidity.snap index 020c828a7..982717200 100644 --- a/.forge-snapshots/addLiquidity.snap +++ b/.forge-snapshots/addLiquidity.snap @@ -1 +1 @@ -192790 \ No newline at end of file +192638 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index dab2b10ac..552760727 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -23414 \ No newline at end of file +23388 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with empty hook.snap b/.forge-snapshots/removeLiquidity with empty hook.snap index 99601b1a8..bf53e294c 100644 --- a/.forge-snapshots/removeLiquidity with empty hook.snap +++ b/.forge-snapshots/removeLiquidity with empty hook.snap @@ -1 +1 @@ -99383 \ No newline at end of file +99211 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with native token.snap b/.forge-snapshots/removeLiquidity with native token.snap index 9cb6f5adf..915a91210 100644 --- a/.forge-snapshots/removeLiquidity with native token.snap +++ b/.forge-snapshots/removeLiquidity with native token.snap @@ -1 +1 @@ -200756 \ No newline at end of file +200584 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity.snap b/.forge-snapshots/removeLiquidity.snap index c09e30a7e..bb3614637 100644 --- a/.forge-snapshots/removeLiquidity.snap +++ b/.forge-snapshots/removeLiquidity.snap @@ -1 +1 @@ -197033 \ No newline at end of file +196861 \ No newline at end of file diff --git a/src/libraries/Pool.sol b/src/libraries/Pool.sol index 212b79335..c4699630d 100644 --- a/src/libraries/Pool.sol +++ b/src/libraries/Pool.sol @@ -210,7 +210,7 @@ library Pool { // current tick is below the passed range; liquidity can only become in range by crossing from left to // right, when we'll need _more_ currency0 (it's becoming more valuable) so user must provide it result = toBalanceDelta( - -SqrtPriceMath.getAmount0Delta( + SqrtPriceMath.getAmount0Delta( TickMath.getSqrtRatioAtTick(params.tickLower), TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta @@ -219,10 +219,10 @@ library Pool { ); } else if (self.slot0.tick < params.tickUpper) { result = toBalanceDelta( - -SqrtPriceMath.getAmount0Delta( + SqrtPriceMath.getAmount0Delta( self.slot0.sqrtPriceX96, TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta ).toInt128(), - -SqrtPriceMath.getAmount1Delta( + SqrtPriceMath.getAmount1Delta( TickMath.getSqrtRatioAtTick(params.tickLower), self.slot0.sqrtPriceX96, params.liquidityDelta ).toInt128() ); @@ -235,7 +235,7 @@ library Pool { // left, when we'll need _more_ currency1 (it's becoming more valuable) so user must provide it result = toBalanceDelta( 0, - -SqrtPriceMath.getAmount1Delta( + SqrtPriceMath.getAmount1Delta( TickMath.getSqrtRatioAtTick(params.tickLower), TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta diff --git a/src/libraries/SqrtPriceMath.sol b/src/libraries/SqrtPriceMath.sol index a391012c7..0839eb73c 100644 --- a/src/libraries/SqrtPriceMath.sol +++ b/src/libraries/SqrtPriceMath.sol @@ -200,8 +200,8 @@ library SqrtPriceMath { { unchecked { return liquidity < 0 - ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() - : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); + ? getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); } } @@ -217,8 +217,8 @@ library SqrtPriceMath { { unchecked { return liquidity < 0 - ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() - : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); + ? getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); } } } diff --git a/test/SqrtPriceMath.t.sol b/test/SqrtPriceMath.t.sol index 68a8edeca..1f47e15fe 100644 --- a/test/SqrtPriceMath.t.sol +++ b/test/SqrtPriceMath.t.sol @@ -249,7 +249,7 @@ contract SqrtPriceMathTestTest is Test, GasSnapshot { assertEq(amount0, 0); } - function test_getAmount0Delta_returns0_1Amount1ForPriceOf1To1_21() public { + function test_getAmount0Delta_1Amount1ForPriceOf1To1_21() public { uint256 amount0 = SqrtPriceMath.getAmount0Delta( Constants.SQRT_RATIO_1_1, Constants.SQRT_RATIO_121_100, uint128(1 ether), true ); @@ -300,7 +300,7 @@ contract SqrtPriceMathTestTest is Test, GasSnapshot { assertEq(amount1, 0); } - function test_getAmount1Delta_returns0_1Amount1ForPriceOf1To1_21() public { + function test_getAmount1Delta_1Amount1ForPriceOf1To1_21() public { uint256 amount1 = SqrtPriceMath.getAmount1Delta( Constants.SQRT_RATIO_1_1, Constants.SQRT_RATIO_121_100, uint128(1 ether), true ); @@ -320,6 +320,12 @@ contract SqrtPriceMathTestTest is Test, GasSnapshot { snapEnd(); } + function test_getAmount1Delta_gasCostForAmount1WhereRoundUpIsTrue() public { + snapStart("getAmount1Delta_gasCostForAmount1WhereRoundUpIsTrue"); + SqrtPriceMath.getAmount1Delta(Constants.SQRT_RATIO_121_100, Constants.SQRT_RATIO_1_1, uint128(1 ether), true); + snapEnd(); + } + function test_getAmount1Delta_gasCostForAmount1WhereRoundUpIsFalse() public { snapStart("getAmount1Delta_gasCostForAmount1WhereRoundUpIsFalse"); SqrtPriceMath.getAmount1Delta(Constants.SQRT_RATIO_121_100, Constants.SQRT_RATIO_1_1, uint128(1 ether), false); From 0043fa4e314a71281be49d61df22b209737e2b91 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Wed, 6 Mar 2024 14:47:39 -0300 Subject: [PATCH 17/19] remove duplicate test --- test/SqrtPriceMath.t.sol | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/SqrtPriceMath.t.sol b/test/SqrtPriceMath.t.sol index 1f47e15fe..cb63d0cbf 100644 --- a/test/SqrtPriceMath.t.sol +++ b/test/SqrtPriceMath.t.sol @@ -320,12 +320,6 @@ contract SqrtPriceMathTestTest is Test, GasSnapshot { snapEnd(); } - function test_getAmount1Delta_gasCostForAmount1WhereRoundUpIsTrue() public { - snapStart("getAmount1Delta_gasCostForAmount1WhereRoundUpIsTrue"); - SqrtPriceMath.getAmount1Delta(Constants.SQRT_RATIO_121_100, Constants.SQRT_RATIO_1_1, uint128(1 ether), true); - snapEnd(); - } - function test_getAmount1Delta_gasCostForAmount1WhereRoundUpIsFalse() public { snapStart("getAmount1Delta_gasCostForAmount1WhereRoundUpIsFalse"); SqrtPriceMath.getAmount1Delta(Constants.SQRT_RATIO_121_100, Constants.SQRT_RATIO_1_1, uint128(1 ether), false); From 1a76db2d6ca52ab3e097e49c803d629611517cd4 Mon Sep 17 00:00:00 2001 From: Emily Williams <emag3m@gmail.com> Date: Wed, 6 Mar 2024 14:56:03 -0500 Subject: [PATCH 18/19] amountSpecified matches deltas (#491) Co-authored-by: hensha256 <henshawalice@gmail.com> --- .../SwapMath_oneForZero_exactInCapped.snap | 2 +- .../SwapMath_oneForZero_exactInPartial.snap | 2 +- .../SwapMath_oneForZero_exactOutCapped.snap | 2 +- .../SwapMath_oneForZero_exactOutPartial.snap | 2 +- .../SwapMath_zeroForOne_exactInCapped.snap | 2 +- .../SwapMath_zeroForOne_exactInPartial.snap | 2 +- .../SwapMath_zeroForOne_exactOutCapped.snap | 2 +- .../SwapMath_zeroForOne_exactOutPartial.snap | 2 +- ...swap hook, already cached dynamic fee.snap | 2 +- .../cached dynamic fee, no hooks.snap | 2 +- .../poolManager bytecode size.snap | 2 +- .forge-snapshots/simple swap with native.snap | 2 +- .forge-snapshots/simple swap.snap | 2 +- .forge-snapshots/simpleSwapEOAInitiated.snap | 2 +- .../simpleSwapNativeEOAInitiated.snap | 2 +- ...p against liquidity with native token.snap | 2 +- .forge-snapshots/swap against liquidity.snap | 2 +- .../swap burn 6909 for input.snap | 2 +- .../swap burn native 6909 for input.snap | 2 +- .../swap mint native output as 6909.snap | 2 +- .../swap mint output as 6909.snap | 2 +- .forge-snapshots/swap with dynamic fee.snap | 2 +- .forge-snapshots/swap with hooks.snap | 2 +- .../update dynamic fee in before swap.snap | 2 +- src/PoolManager.sol | 2 +- src/libraries/Pool.sol | 18 +++---- src/libraries/SafeCast.sol | 8 --- src/libraries/SwapMath.sol | 14 ++--- src/test/PoolNestedActionsTest.sol | 4 +- src/test/PoolSwapTest.sol | 12 ++--- test/DynamicFees.t.sol | 16 +++--- test/PoolManager.t.sol | 54 +++++++++---------- test/SwapMath.t.sol | 30 +++++------ test/utils/Deployers.sol | 4 +- test/utils/SwapHelper.t.sol | 54 +++++++++---------- 35 files changed, 128 insertions(+), 136 deletions(-) diff --git a/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap b/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap index 9fbf3f867..27d48e9a4 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap @@ -1 +1 @@ -2203 \ No newline at end of file +1947 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap b/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap index b19e196ad..9560ba822 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap @@ -1 +1 @@ -3028 \ No newline at end of file +3238 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap b/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap index 05949f123..758e03adc 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap @@ -1 +1 @@ -1963 \ No newline at end of file +2208 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap b/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap index b19e196ad..9560ba822 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap @@ -1 +1 @@ -3028 \ No newline at end of file +3238 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap b/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap index 61b5691d1..12057ad70 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap @@ -1 +1 @@ -2193 \ No newline at end of file +1937 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap b/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap index 1a81226e6..c173e591f 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap @@ -1 +1 @@ -3181 \ No newline at end of file +2826 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap b/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap index 6d92a178f..c802063a0 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap @@ -1 +1 @@ -1953 \ No newline at end of file +2198 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap b/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap index 1a81226e6..c173e591f 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap @@ -1 +1 @@ -3181 \ No newline at end of file +2826 \ No newline at end of file diff --git a/.forge-snapshots/before swap hook, already cached dynamic fee.snap b/.forge-snapshots/before swap hook, already cached dynamic fee.snap index e9aec1167..981929aca 100644 --- a/.forge-snapshots/before swap hook, already cached dynamic fee.snap +++ b/.forge-snapshots/before swap hook, already cached dynamic fee.snap @@ -1 +1 @@ -185335 \ No newline at end of file +184850 \ No newline at end of file diff --git a/.forge-snapshots/cached dynamic fee, no hooks.snap b/.forge-snapshots/cached dynamic fee, no hooks.snap index 61d8c9782..af2004dd5 100644 --- a/.forge-snapshots/cached dynamic fee, no hooks.snap +++ b/.forge-snapshots/cached dynamic fee, no hooks.snap @@ -1 +1 @@ -139362 \ No newline at end of file +138877 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 552760727..bc5e03d4b 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -23388 \ No newline at end of file +23323 \ No newline at end of file diff --git a/.forge-snapshots/simple swap with native.snap b/.forge-snapshots/simple swap with native.snap index 0a345d1f6..3209f2c45 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -187932 \ No newline at end of file +187447 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index 0a7e23711..2e5f87b51 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -196472 \ No newline at end of file +195987 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapEOAInitiated.snap b/.forge-snapshots/simpleSwapEOAInitiated.snap index a856fb2a7..07992eea9 100644 --- a/.forge-snapshots/simpleSwapEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapEOAInitiated.snap @@ -1 +1 @@ -167083 \ No newline at end of file +166598 \ No newline at end of file diff --git a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap index a7dbfb203..16f69621a 100644 --- a/.forge-snapshots/simpleSwapNativeEOAInitiated.snap +++ b/.forge-snapshots/simpleSwapNativeEOAInitiated.snap @@ -1 +1 @@ -165611 \ No newline at end of file +165126 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity with native token.snap b/.forge-snapshots/swap against liquidity with native token.snap index 39f69af23..e86de2e5d 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -118595 \ No newline at end of file +118106 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index ed0bccfac..7ef41f543 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -106062 \ No newline at end of file +105573 \ No newline at end of file diff --git a/.forge-snapshots/swap burn 6909 for input.snap b/.forge-snapshots/swap burn 6909 for input.snap index 22728cc3a..0eda177e1 100644 --- a/.forge-snapshots/swap burn 6909 for input.snap +++ b/.forge-snapshots/swap burn 6909 for input.snap @@ -1 +1 @@ -126218 \ No newline at end of file +125700 \ No newline at end of file diff --git a/.forge-snapshots/swap burn native 6909 for input.snap b/.forge-snapshots/swap burn native 6909 for input.snap index 056426fae..e188f56da 100644 --- a/.forge-snapshots/swap burn native 6909 for input.snap +++ b/.forge-snapshots/swap burn native 6909 for input.snap @@ -1 +1 @@ -122171 \ No newline at end of file +121653 \ No newline at end of file diff --git a/.forge-snapshots/swap mint native output as 6909.snap b/.forge-snapshots/swap mint native output as 6909.snap index 5b925b581..8da1a1e6a 100644 --- a/.forge-snapshots/swap mint native output as 6909.snap +++ b/.forge-snapshots/swap mint native output as 6909.snap @@ -1 +1 @@ -190494 \ No newline at end of file +190005 \ No newline at end of file diff --git a/.forge-snapshots/swap mint output as 6909.snap b/.forge-snapshots/swap mint output as 6909.snap index 8346aab23..57cb7b38e 100644 --- a/.forge-snapshots/swap mint output as 6909.snap +++ b/.forge-snapshots/swap mint output as 6909.snap @@ -1 +1 @@ -207299 \ No newline at end of file +206814 \ No newline at end of file diff --git a/.forge-snapshots/swap with dynamic fee.snap b/.forge-snapshots/swap with dynamic fee.snap index 9c4a53763..0c7ae4c05 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -184436 \ No newline at end of file +183951 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index fa9619017..aff2d029f 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -106040 \ No newline at end of file +105551 \ No newline at end of file diff --git a/.forge-snapshots/update dynamic fee in before swap.snap b/.forge-snapshots/update dynamic fee in before swap.snap index d554a0afa..c17d44032 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -191140 \ No newline at end of file +190655 \ No newline at end of file diff --git a/src/PoolManager.sol b/src/PoolManager.sol index e530decc1..4b0c705eb 100644 --- a/src/PoolManager.sol +++ b/src/PoolManager.sol @@ -231,7 +231,7 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { } emit Swap( - id, msg.sender, -delta.amount0(), -delta.amount1(), state.sqrtPriceX96, state.liquidity, state.tick, swapFee + id, msg.sender, delta.amount0(), delta.amount1(), state.sqrtPriceX96, state.liquidity, state.tick, swapFee ); key.hooks.afterSwap(key, params, delta, hookData); diff --git a/src/libraries/Pool.sol b/src/libraries/Pool.sol index c4699630d..72549922e 100644 --- a/src/libraries/Pool.sol +++ b/src/libraries/Pool.sol @@ -326,7 +326,7 @@ library Pool { protocolFee: params.zeroForOne ? uint8(slot0Start.protocolFee % 256) : uint8(slot0Start.protocolFee >> 8) }); - bool exactInput = params.amountSpecified > 0; + bool exactInput = params.amountSpecified < 0; state = SwapState({ amountSpecifiedRemaining: params.amountSpecified, @@ -371,14 +371,14 @@ library Pool { if (exactInput) { // safe because we test that amountSpecified > amountIn + feeAmount in SwapMath unchecked { - state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256(); + state.amountSpecifiedRemaining += (step.amountIn + step.feeAmount).toInt256(); } - state.amountCalculated = state.amountCalculated - step.amountOut.toInt256(); + state.amountCalculated = state.amountCalculated + step.amountOut.toInt256(); } else { unchecked { - state.amountSpecifiedRemaining += step.amountOut.toInt256(); + state.amountSpecifiedRemaining -= step.amountOut.toInt256(); } - state.amountCalculated = state.amountCalculated + (step.amountIn + step.feeAmount).toInt256(); + state.amountCalculated = state.amountCalculated - (step.amountIn + step.feeAmount).toInt256(); } // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee @@ -445,13 +445,13 @@ library Pool { unchecked { if (params.zeroForOne == exactInput) { result = toBalanceDelta( - (params.amountSpecified - state.amountSpecifiedRemaining).toInt128().flipSign(), - state.amountCalculated.toInt128().flipSign() + (params.amountSpecified - state.amountSpecifiedRemaining).toInt128(), + state.amountCalculated.toInt128() ); } else { result = toBalanceDelta( - state.amountCalculated.toInt128().flipSign(), - (params.amountSpecified - state.amountSpecifiedRemaining).toInt128().flipSign() + state.amountCalculated.toInt128(), + (params.amountSpecified - state.amountSpecifiedRemaining).toInt128() ); } } diff --git a/src/libraries/SafeCast.sol b/src/libraries/SafeCast.sol index 82bcac38c..5eb5bd474 100644 --- a/src/libraries/SafeCast.sol +++ b/src/libraries/SafeCast.sol @@ -37,12 +37,4 @@ library SafeCast { if (y > uint128(type(int128).max)) revert SafeCastOverflow(); z = int128(int256(y)); } - - /// @notice Flip the sign of an int128, revert on overflow - /// @param y The int128 to flip the sign of - /// @return The flipped integer, still an int128 - function flipSign(int128 y) internal pure returns (int128) { - // this is not unchecked, so it will revert if y==type(int128).min - return -y; - } } diff --git a/src/libraries/SwapMath.sol b/src/libraries/SwapMath.sol index 6c011dd3b..d7ba4405d 100644 --- a/src/libraries/SwapMath.sol +++ b/src/libraries/SwapMath.sol @@ -27,10 +27,10 @@ library SwapMath { ) internal pure returns (uint160 sqrtRatioNextX96, uint256 amountIn, uint256 amountOut, uint256 feeAmount) { unchecked { bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96; - bool exactIn = amountRemaining >= 0; + bool exactIn = amountRemaining < 0; if (exactIn) { - uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6); + uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(-amountRemaining), 1e6 - feePips, 1e6); amountIn = zeroForOne ? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true) : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true); @@ -45,11 +45,11 @@ library SwapMath { amountOut = zeroForOne ? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false) : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false); - if (uint256(-amountRemaining) >= amountOut) { + if (uint256(amountRemaining) >= amountOut) { sqrtRatioNextX96 = sqrtRatioTargetX96; } else { sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput( - sqrtRatioCurrentX96, liquidity, uint256(-amountRemaining), zeroForOne + sqrtRatioCurrentX96, liquidity, uint256(amountRemaining), zeroForOne ); } } @@ -74,13 +74,13 @@ library SwapMath { } // cap the output amount to not exceed the remaining output amount - if (!exactIn && amountOut > uint256(-amountRemaining)) { - amountOut = uint256(-amountRemaining); + if (!exactIn && amountOut > uint256(amountRemaining)) { + amountOut = uint256(amountRemaining); } if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) { // we didn't reach the target, so take the remainder of the maximum input as fee - feeAmount = uint256(amountRemaining) - amountIn; + feeAmount = uint256(-amountRemaining) - amountIn; } else { feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips); } diff --git a/src/test/PoolNestedActionsTest.sol b/src/test/PoolNestedActionsTest.sol index 9cdc8f8e9..5ad95f9ab 100644 --- a/src/test/PoolNestedActionsTest.sol +++ b/src/test/PoolNestedActionsTest.sol @@ -70,7 +70,7 @@ contract NestedActionExecutor is Test, PoolTestBase { IPoolManager.ModifyLiquidityParams({tickLower: -120, tickUpper: 120, liquidityDelta: -1e18}); IPoolManager.SwapParams internal SWAP_PARAMS = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: Constants.SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: Constants.SQRT_RATIO_1_2}); uint256 internal DONATE_AMOUNT0 = 12345e6; uint256 internal DONATE_AMOUNT1 = 98765e4; @@ -124,7 +124,7 @@ contract NestedActionExecutor is Test, PoolTestBase { assertEq(deltaLockerBefore0, deltaLockerAfter0, "Locker delta 0"); assertEq(deltaLockerBefore1, deltaLockerAfter1, "Locker delta 1"); - assertEq(deltaThisBefore0 - SWAP_PARAMS.amountSpecified, deltaThisAfter0, "Executor delta 0"); + assertEq(deltaThisBefore0 + SWAP_PARAMS.amountSpecified, deltaThisAfter0, "Executor delta 0"); assertEq(deltaThisBefore1 + 98, deltaThisAfter1, "Executor delta 1"); assertEq(delta.amount0(), deltaThisAfter0, "Swap delta 0"); assertEq(delta.amount1(), deltaThisAfter1, "Swap delta 1"); diff --git a/src/test/PoolSwapTest.sol b/src/test/PoolSwapTest.sol index 822425fc0..dde0e3119 100644 --- a/src/test/PoolSwapTest.sol +++ b/src/test/PoolSwapTest.sol @@ -71,24 +71,24 @@ contract PoolSwapTest is Test, PoolTestBase { assertEq(reserveBefore1, reserveAfter1); if (data.params.zeroForOne) { - if (data.params.amountSpecified > 0) { + if (data.params.amountSpecified < 0) { // exact input, 0 for 1 - assertEq(-deltaAfter0, data.params.amountSpecified); + assertEq(deltaAfter0, data.params.amountSpecified); assertGt(deltaAfter1, 0); } else { // exact output, 0 for 1 assertLt(deltaAfter0, 0); - assertEq(-deltaAfter1, data.params.amountSpecified); + assertEq(deltaAfter1, data.params.amountSpecified); } } else { - if (data.params.amountSpecified > 0) { + if (data.params.amountSpecified < 0) { // exact input, 1 for 0 - assertEq(-deltaAfter1, data.params.amountSpecified); + assertEq(deltaAfter1, data.params.amountSpecified); assertGt(deltaAfter0, 0); } else { // exact output, 1 for 0 assertLt(deltaAfter1, 0); - assertEq(-deltaAfter0, data.params.amountSpecified); + assertEq(deltaAfter0, data.params.amountSpecified); } } diff --git a/test/DynamicFees.t.sol b/test/DynamicFees.t.sol index 9ad0df3e3..47a90eba5 100644 --- a/test/DynamicFees.t.sol +++ b/test/DynamicFees.t.sol @@ -103,12 +103,12 @@ contract TestDynamicFees is Test, Deployers, GasSnapshot { manager.updateDynamicSwapFee(key); IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); vm.expectEmit(true, true, true, true, address(manager)); - emit Swap(key.toId(), address(swapRouter), 100, -98, 79228162514264329749955861424, 1e18, -1, 123); + emit Swap(key.toId(), address(swapRouter), -100, 98, 79228162514264329749955861424, 1e18, -1, 123); snapStart("swap with dynamic fee"); swapRouter.swap(key, params, testSettings, ZERO_BYTES); @@ -120,12 +120,12 @@ contract TestDynamicFees is Test, Deployers, GasSnapshot { manager.updateDynamicSwapFee(key); IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); vm.expectEmit(true, true, true, true, address(manager)); - emit Swap(key.toId(), address(swapRouter), 100, -98, 79228162514264329749955861424, 1e18, -1, 456); + emit Swap(key.toId(), address(swapRouter), -100, 98, 79228162514264329749955861424, 1e18, -1, 456); bytes memory data = abi.encode(true, uint24(456)); snapStart("update dynamic fee in before swap"); @@ -138,12 +138,12 @@ contract TestDynamicFees is Test, Deployers, GasSnapshot { manager.updateDynamicSwapFee(key); IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); vm.expectEmit(true, true, true, true, address(manager)); - emit Swap(key.toId(), address(swapRouter), 100, -98, 79228162514264329749955861424, 1e18, -1, 123); + emit Swap(key.toId(), address(swapRouter), -100, 98, 79228162514264329749955861424, 1e18, -1, 123); bytes memory data = abi.encode(false, uint24(0)); snapStart("before swap hook, already cached dynamic fee"); @@ -166,12 +166,12 @@ contract TestDynamicFees is Test, Deployers, GasSnapshot { manager.updateDynamicSwapFee(key); IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); vm.expectEmit(true, true, true, true, address(manager)); - emit Swap(key.toId(), address(swapRouter), 100, -98, 79228162514264329749955861424, 1e18, -1, 123); + emit Swap(key.toId(), address(swapRouter), -100, 98, 79228162514264329749955861424, 1e18, -1, 123); snapStart("cached dynamic fee, no hooks"); swapRouter.swap(key, params, testSettings, ZERO_BYTES); diff --git a/test/PoolManager.t.sol b/test/PoolManager.t.sol index 93932f912..e4b1b34e2 100644 --- a/test/PoolManager.t.sol +++ b/test/PoolManager.t.sol @@ -411,7 +411,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { key.fee = 100; IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: sqrtPriceX96}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: sqrtPriceX96}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -422,14 +422,14 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function test_swap_succeedsIfInitialized() public { IPoolManager.SwapParams memory swapParams = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true, currencyAlreadySent: false}); vm.expectEmit(true, true, true, true); emit Swap( - key.toId(), address(swapRouter), int128(100), int128(-98), 79228162514264329749955861424, 1e18, -1, 3000 + key.toId(), address(swapRouter), int128(-100), int128(98), 79228162514264329749955861424, 1e18, -1, 3000 ); swapRouter.swap(key, swapParams, testSettings, ZERO_BYTES); @@ -437,7 +437,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function test_swap_succeedsWithNativeTokensIfInitialized() public { IPoolManager.SwapParams memory swapParams = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -446,8 +446,8 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { emit Swap( nativeKey.toId(), address(swapRouter), - int128(100), - int128(-98), + int128(-100), + int128(98), 79228162514264329749955861424, 1e18, -1, @@ -470,7 +470,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { (key,) = initPoolAndAddLiquidity(currency0, currency1, IHooks(mockAddr), 3000, SQRT_RATIO_1_1, ZERO_BYTES); IPoolManager.SwapParams memory swapParams = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -527,7 +527,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { (key,) = initPoolAndAddLiquidity(currency0, currency1, mockHooks, 100, SQRT_RATIO_1_1, ZERO_BYTES); IPoolManager.SwapParams memory swapParams = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 10, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -10, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -536,14 +536,14 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { mockHooks.setReturnValue(mockHooks.afterSwap.selector, mockHooks.afterSwap.selector); vm.expectEmit(true, true, true, true); - emit Swap(key.toId(), address(swapRouter), 10, -8, 79228162514264336880490487708, 1e18, -1, 100); + emit Swap(key.toId(), address(swapRouter), -10, 8, 79228162514264336880490487708, 1e18, -1, 100); swapRouter.swap(key, swapParams, testSettings, ZERO_BYTES); } function test_swap_gas() public { IPoolManager.SwapParams memory swapParams = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -555,7 +555,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function test_swap_withNative_gas() public { IPoolManager.SwapParams memory swapParams = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -575,7 +575,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { (key,) = initPoolAndAddLiquidity(currency0, currency1, mockHooks, 3000, SQRT_RATIO_1_1, ZERO_BYTES); IPoolManager.SwapParams memory swapParams = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -583,7 +583,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { swapRouter.swap(key, swapParams, testSettings, ZERO_BYTES); swapParams = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_4}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_4}); testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -594,7 +594,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function test_swap_mint6909IfOutputNotTaken_gas() public { IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -611,7 +611,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function test_swap_mint6909IfNativeOutputNotTaken_gas() public { IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: false, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_2_1}); + IPoolManager.SwapParams({zeroForOne: false, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_2_1}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -628,7 +628,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function test_swap_burn6909AsInput_gas() public { IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -644,7 +644,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { manager.setOperator(address(swapRouter), true); // swap from currency1 to currency0 again, using 6909s as input tokens - params = IPoolManager.SwapParams({zeroForOne: false, amountSpecified: -25, sqrtPriceLimitX96: SQRT_RATIO_4_1}); + params = IPoolManager.SwapParams({zeroForOne: false, amountSpecified: 25, sqrtPriceLimitX96: SQRT_RATIO_4_1}); testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: false, currencyAlreadySent: false}); @@ -660,7 +660,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function test_swap_burnNative6909AsInput_gas() public { IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: false, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_2_1}); + IPoolManager.SwapParams({zeroForOne: false, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_2_1}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true, currencyAlreadySent: false}); @@ -676,7 +676,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { manager.setOperator(address(swapRouter), true); // swap from currency0 to currency1, using 6909s as input tokens - params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -25, sqrtPriceLimitX96: SQRT_RATIO_1_4}); + params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 25, sqrtPriceLimitX96: SQRT_RATIO_1_4}); testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: false, currencyAlreadySent: false}); @@ -693,14 +693,14 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function test_swap_againstLiq_gas() public { IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); swapRouter.swap(key, params, testSettings, ZERO_BYTES); - params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_4}); + params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_4}); snapStart("swap against liquidity"); swapRouter.swap(key, params, testSettings, ZERO_BYTES); @@ -709,14 +709,14 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { function test_swap_againstLiqWithNative_gas() public { IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); swapRouter.swap{value: 1 ether}(nativeKey, params, testSettings, ZERO_BYTES); - params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_4}); + params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: SQRT_RATIO_1_4}); snapStart("swap against liquidity with native token"); swapRouter.swap{value: 1 ether}(nativeKey, params, testSettings, ZERO_BYTES); @@ -753,10 +753,10 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { params.liquidityDelta = LIQ_PARAMS.liquidityDelta; modifyLiquidityRouter.modifyLiquidity(key, params, ZERO_BYTES); - IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams(false, 10000, TickMath.MAX_SQRT_RATIO - 1); + IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams(false, -10000, TickMath.MAX_SQRT_RATIO - 1); swapRouter.swap(key, swapParams, PoolSwapTest.TestSettings(true, true, false), ZERO_BYTES); - uint256 expectedTotalSwapFee = uint256(swapParams.amountSpecified) * key.fee / 1e6; + uint256 expectedTotalSwapFee = uint256(-swapParams.amountSpecified) * key.fee / 1e6; uint256 expectedProtocolFee = expectedTotalSwapFee / protocolFee1; assertEq(manager.protocolFeesAccrued(currency0), 0); assertEq(manager.protocolFeesAccrued(currency1), expectedProtocolFee); @@ -1013,7 +1013,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { swapRouter.swap{value: 10000}( nativeKey, - IPoolManager.SwapParams(true, 10000, SQRT_RATIO_1_2), + IPoolManager.SwapParams(true, -10000, SQRT_RATIO_1_2), PoolSwapTest.TestSettings(true, true, false), ZERO_BYTES ); @@ -1042,7 +1042,7 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot { swapRouter.swap{value: 10000}( nativeKey, - IPoolManager.SwapParams(true, 10000, SQRT_RATIO_1_2), + IPoolManager.SwapParams(true, -10000, SQRT_RATIO_1_2), PoolSwapTest.TestSettings(true, true, false), ZERO_BYTES ); diff --git a/test/SwapMath.t.sol b/test/SwapMath.t.sol index ad63dc41d..ebabd5da6 100644 --- a/test/SwapMath.t.sol +++ b/test/SwapMath.t.sol @@ -66,7 +66,7 @@ contract SwapMathTest is Test, GasSnapshot { uint160 priceTarget = SQRT_RATIO_1000_100; uint160 price = SQRT_RATIO_1_1; uint128 liquidity = 2 ether; - int256 amount = 1 ether; + int256 amount = 1 ether * -1; uint24 fee = 600; bool zeroForOne = false; @@ -76,10 +76,10 @@ contract SwapMathTest is Test, GasSnapshot { assertEq(amountIn, 999400000000000000); assertEq(amountOut, 666399946655997866); assertEq(feeAmount, 600000000000000); - assertEq(amountIn + feeAmount, uint256(amount)); + assertEq(amountIn + feeAmount, uint256(-amount)); uint256 priceAfterWholeInputAmountLessFee = - SqrtPriceMath.getNextSqrtPriceFromInput(price, liquidity, uint256(uint256(amount) - feeAmount), zeroForOne); + SqrtPriceMath.getNextSqrtPriceFromInput(price, liquidity, uint256(uint256(-amount) - feeAmount), zeroForOne); assert(sqrtQ < priceTarget); assertEq(sqrtQ, priceAfterWholeInputAmountLessFee); @@ -89,7 +89,7 @@ contract SwapMathTest is Test, GasSnapshot { uint160 priceTarget = SQRT_RATIO_10000_100; uint160 price = SQRT_RATIO_1_1; uint128 liquidity = 2 ether; - int256 amount = (1 ether) * -1; + int256 amount = (1 ether); uint24 fee = 600; bool zeroForOne = false; @@ -98,10 +98,10 @@ contract SwapMathTest is Test, GasSnapshot { assertEq(amountIn, 2000000000000000000); assertEq(feeAmount, 1200720432259356); - assertEq(amountOut, uint256(amount * -1)); + assertEq(amountOut, uint256(amount)); uint256 priceAfterWholeOutputAmount = - SqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, uint256(amount * -1), zeroForOne); + SqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, uint256(amount), zeroForOne); assert(sqrtQ < priceTarget); assertEq(sqrtQ, priceAfterWholeOutputAmount); @@ -109,7 +109,7 @@ contract SwapMathTest is Test, GasSnapshot { function test_amountOut_isCappedAtTheDesiredAmountOut() public { (uint160 sqrtQ, uint256 amountIn, uint256 amountOut, uint256 feeAmount) = SwapMath.computeSwapStep( - 417332158212080721273783715441582, 1452870262520218020823638996, 159344665391607089467575320103, -1, 1 + 417332158212080721273783715441582, 1452870262520218020823638996, 159344665391607089467575320103, 1, 1 ); assertEq(amountIn, 1); @@ -130,7 +130,7 @@ contract SwapMathTest is Test, GasSnapshot { function test_entireInputAmountTakenAsFee() public { (uint160 sqrtQ, uint256 amountIn, uint256 amountOut, uint256 feeAmount) = - SwapMath.computeSwapStep(2413, 79887613182836312, 1985041575832132834610021537970, 10, 1872); + SwapMath.computeSwapStep(2413, 79887613182836312, 1985041575832132834610021537970, -10, 1872); assertEq(amountIn, 0); assertEq(feeAmount, 10); @@ -144,7 +144,7 @@ contract SwapMathTest is Test, GasSnapshot { uint128 liquidity = 1024; // virtual reserves of one are only 4 // https://www.wolframalpha.com/input/?i=1024+%2F+%2820282409603651670423947251286016+%2F+2**96%29 - int256 amountRemaining = -4; + int256 amountRemaining = 4; uint24 feePips = 3000; (uint160 sqrtQ, uint256 amountIn, uint256 amountOut, uint256 feeAmount) = @@ -162,7 +162,7 @@ contract SwapMathTest is Test, GasSnapshot { uint128 liquidity = 1024; // virtual reserves of zero are only 262144 // https://www.wolframalpha.com/input/?i=1024+*+%2820282409603651670423947251286016+%2F+2**96%29 - int256 amountRemaining = -263000; + int256 amountRemaining = 263000; uint24 feePips = 3000; (uint160 sqrtQ, uint256 amountIn, uint256 amountOut, uint256 feeAmount) = @@ -192,10 +192,10 @@ contract SwapMathTest is Test, GasSnapshot { assertLe(amountIn, type(uint256).max - feeAmount); unchecked { - if (amountRemaining < 0) { - assertLe(amountOut, uint256(-amountRemaining)); + if (amountRemaining >= 0) { + assertLe(amountOut, uint256(amountRemaining)); } else { - assertLe(amountIn + feeAmount, uint256(amountRemaining)); + assertLe(amountIn + feeAmount, uint256(-amountRemaining)); } } @@ -208,8 +208,8 @@ contract SwapMathTest is Test, GasSnapshot { // didn't reach price target, entire amount must be consumed if (sqrtQ != sqrtPriceTargetRaw) { - if (amountRemaining < 0) assertEq(amountOut, uint256(-amountRemaining)); - else assertEq(amountIn + feeAmount, uint256(amountRemaining)); + if (amountRemaining > 0) assertEq(amountOut, uint256(amountRemaining)); + else assertEq(amountIn + feeAmount, uint256(-amountRemaining)); } // next price is between price and price target diff --git a/test/utils/Deployers.sol b/test/utils/Deployers.sol index d3fbc1658..e19425015 100644 --- a/test/utils/Deployers.sol +++ b/test/utils/Deployers.sol @@ -192,9 +192,9 @@ contract Deployers { { // allow native input for exact-input, guide users to the `swapNativeInput` function bool isNativeInput = zeroForOne && _key.currency0.isNative(); - if (isNativeInput) require(0 < amountSpecified, "Use swapNativeInput() for native-token exact-output swaps"); + if (isNativeInput) require(0 > amountSpecified, "Use swapNativeInput() for native-token exact-output swaps"); - uint256 value = isNativeInput ? uint256(amountSpecified) : 0; + uint256 value = isNativeInput ? uint256(-amountSpecified) : 0; return swapRouter.swap{value: value}( _key, diff --git a/test/utils/SwapHelper.t.sol b/test/utils/SwapHelper.t.sol index b26886ae3..1d83d663b 100644 --- a/test/utils/SwapHelper.t.sol +++ b/test/utils/SwapHelper.t.sol @@ -39,98 +39,98 @@ contract SwapHelperTest is Test, Deployers, GasSnapshot { // --- Deployers.swap() tests --- // function test_swap_helper_zeroForOne_exactInput() public { - int256 amountSpecified = 100; + int256 amountSpecified = -100; BalanceDelta result = swap(key, true, amountSpecified, ZERO_BYTES); - assertEq(-int256(result.amount0()), amountSpecified); + assertEq(result.amount0(), amountSpecified); } function test_swap_helper_zeroForOne_exactOutput() public { - int256 amountSpecified = -100; + int256 amountSpecified = 100; BalanceDelta result = swap(key, true, amountSpecified, ZERO_BYTES); - assertEq(-int256(result.amount1()), amountSpecified); + assertEq(result.amount1(), amountSpecified); } function test_swap_helper_oneForZero_exactInput() public { - int256 amountSpecified = 100; + int256 amountSpecified = -100; BalanceDelta result = swap(key, false, amountSpecified, ZERO_BYTES); - assertEq(-int256(result.amount1()), amountSpecified); + assertEq(result.amount1(), amountSpecified); } function test_swap_helper_oneForZero_exactOutput() public { - int256 amountSpecified = -100; + int256 amountSpecified = 100; BalanceDelta result = swap(key, false, amountSpecified, ZERO_BYTES); - assertEq(-int256(result.amount0()), amountSpecified); + assertEq(result.amount0(), amountSpecified); } function test_swap_helper_native_zeroForOne_exactInput() public { - int256 amountSpecified = 100; + int256 amountSpecified = -100; BalanceDelta result = swap(nativeKey, true, amountSpecified, ZERO_BYTES); - assertEq(-int256(result.amount0()), amountSpecified); + assertEq(result.amount0(), amountSpecified); } function test_swap_helper_native_zeroForOne_exactOutput() public { - int256 amountSpecified = -100; + int256 amountSpecified = 100; vm.expectRevert(); swap(nativeKey, true, amountSpecified, ZERO_BYTES); } function test_swap_helper_native_oneForZero_exactInput() public { - int256 amountSpecified = 100; + int256 amountSpecified = -100; BalanceDelta result = swap(nativeKey, false, amountSpecified, ZERO_BYTES); - assertEq(-int256(result.amount1()), amountSpecified); + assertEq(result.amount1(), amountSpecified); } function test_swap_helper_native_oneForZero_exactOutput() public { - int256 amountSpecified = -100; + int256 amountSpecified = 100; BalanceDelta result = swap(nativeKey, false, amountSpecified, ZERO_BYTES); - assertEq(-int256(result.amount0()), amountSpecified); + assertEq(result.amount0(), amountSpecified); } // --- Deployers.swapNativeInput() tests --- // function test_swapNativeInput_helper_zeroForOne_exactInput() public { - int256 amountSpecified = 100; + int256 amountSpecified = -100; BalanceDelta result = swapNativeInput(nativeKey, true, amountSpecified, ZERO_BYTES, 100 wei); - assertEq(-int256(result.amount0()), amountSpecified); + assertEq(result.amount0(), amountSpecified); } function test_swapNativeInput_helper_zeroForOne_exactOutput() public { - int256 amountSpecified = -100; + int256 amountSpecified = 100; BalanceDelta result = swapNativeInput(nativeKey, true, amountSpecified, ZERO_BYTES, 200 wei); // overpay - assertEq(-int256(result.amount1()), amountSpecified); + assertEq(result.amount1(), amountSpecified); } function test_swapNativeInput_helper_oneForZero_exactInput() public { - int256 amountSpecified = 100; + int256 amountSpecified = -100; BalanceDelta result = swapNativeInput(nativeKey, false, amountSpecified, ZERO_BYTES, 0 wei); - assertEq(-int256(result.amount1()), amountSpecified); + assertEq(result.amount1(), amountSpecified); } function test_swapNativeInput_helper_oneForZero_exactOutput() public { - int256 amountSpecified = -100; + int256 amountSpecified = 100; BalanceDelta result = swapNativeInput(nativeKey, false, amountSpecified, ZERO_BYTES, 0 wei); - assertEq(-int256(result.amount0()), amountSpecified); + assertEq(result.amount0(), amountSpecified); } function test_swapNativeInput_helper_nonnative_zeroForOne_exactInput() public { - int256 amountSpecified = 100; + int256 amountSpecified = -100; vm.expectRevert(); swapNativeInput(key, true, amountSpecified, ZERO_BYTES, 0 wei); } function test_swapNativeInput_helper_nonnative_zeroForOne_exactOutput() public { - int256 amountSpecified = -100; + int256 amountSpecified = 100; vm.expectRevert(); swapNativeInput(key, true, amountSpecified, ZERO_BYTES, 0 wei); } function test_swapNativeInput_helper_nonnative_oneForZero_exactInput() public { - int256 amountSpecified = 100; + int256 amountSpecified = -100; vm.expectRevert(); swapNativeInput(key, false, amountSpecified, ZERO_BYTES, 0 wei); } function test_swapNativeInput_helper_nonnative_oneForZero_exactOutput() public { - int256 amountSpecified = -100; + int256 amountSpecified = 100; vm.expectRevert(); swapNativeInput(key, false, amountSpecified, ZERO_BYTES, 0 wei); } From 4a1ccfa5ca5ca4b979fed2b5b95eb849aeb5ee42 Mon Sep 17 00:00:00 2001 From: hensha256 <henshawalice@gmail.com> Date: Fri, 8 Mar 2024 11:49:11 -0300 Subject: [PATCH 19/19] PR nits --- src/PoolManager.sol | 4 ++-- src/libraries/Pool.sol | 1 - src/test/PoolSwapTest.sol | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/PoolManager.sol b/src/PoolManager.sol index 4b0c705eb..ff2e4e4af 100644 --- a/src/PoolManager.sol +++ b/src/PoolManager.sol @@ -259,7 +259,7 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { /// @inheritdoc IPoolManager function take(Currency currency, address to, uint256 amount) external override noDelegateCall isLocked { - // flipping a postive number in an int is always safe + // subtraction must be safe _accountDelta(currency, -(amount.toInt128())); reservesOf[currency] -= amount; currency.transfer(to, amount); @@ -275,7 +275,7 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims { /// @inheritdoc IPoolManager function mint(address to, uint256 id, uint256 amount) external override noDelegateCall isLocked { - // flipping a postive number in an int is always safe + // subtraction must be safe _accountDelta(CurrencyLibrary.fromId(id), -(amount.toInt128())); _mint(to, id, amount); } diff --git a/src/libraries/Pool.sol b/src/libraries/Pool.sol index 72549922e..5deb01e2b 100644 --- a/src/libraries/Pool.sol +++ b/src/libraries/Pool.sol @@ -441,7 +441,6 @@ library Pool { self.feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; } - // in an unchecked block so sign flipping is not safe without a helper function unchecked { if (params.zeroForOne == exactInput) { result = toBalanceDelta( diff --git a/src/test/PoolSwapTest.sol b/src/test/PoolSwapTest.sol index dde0e3119..757b8379d 100644 --- a/src/test/PoolSwapTest.sol +++ b/src/test/PoolSwapTest.sol @@ -9,7 +9,6 @@ import {IHooks} from "../interfaces/IHooks.sol"; import {Hooks} from "../libraries/Hooks.sol"; import {PoolTestBase} from "./PoolTestBase.sol"; import {Test} from "forge-std/Test.sol"; -import {console2} from "forge-std/console2.sol"; import {Hooks} from "../libraries/Hooks.sol"; import {IHooks} from "../interfaces/IHooks.sol";