diff --git a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap index 0386c0a..d058e92 100644 --- a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap +++ b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap @@ -1 +1 @@ -193198 \ No newline at end of file +193973 \ No newline at end of file diff --git a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap index c823d60..f376cc1 100644 --- a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap +++ b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap @@ -1 +1 @@ -192654 \ No newline at end of file +194041 \ No newline at end of file diff --git a/.forge-snapshots/UniversalRouterBytecodeSize.snap b/.forge-snapshots/UniversalRouterBytecodeSize.snap index 95d2f82..714963e 100644 --- a/.forge-snapshots/UniversalRouterBytecodeSize.snap +++ b/.forge-snapshots/UniversalRouterBytecodeSize.snap @@ -1 +1 @@ -24434 \ No newline at end of file +24566 \ No newline at end of file diff --git a/script/deployParameters/testnet/DeployBscTestnet.s.sol b/script/deployParameters/testnet/DeployBscTestnet.s.sol index 6bbb026..55d76ee 100644 --- a/script/deployParameters/testnet/DeployBscTestnet.s.sol +++ b/script/deployParameters/testnet/DeployBscTestnet.s.sol @@ -23,7 +23,7 @@ contract DeployBscTestnet is DeployUniversalRouter { v2InitCodeHash: 0xd0d4c4cd0848c93cb4fd1f498d7013ee6bfb25783ea21593d5834f5d250ece66, v3InitCodeHash: 0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2, stableFactory: 0xe6A00f8b819244e8Ab9Ea930e46449C2F20B6609, - stableInfo: 0xe6A00f8b819244e8Ab9Ea930e46449C2F20B6609, + stableInfo: 0x0A548d59D04096Bc01206D58C3D63c478e1e06dB, v4Vault: 0x0a125Bb36e409957Ed951eF1FBe20e81D682EAb6, v4ClPoolManager: 0x26Ca53c8C5CE90E22aA1FadDA68AB9a08f7BA06f, v4BinPoolManager: 0x1DF0be383e9d17DA4448E57712849aBE5b3Fa33b, diff --git a/src/base/Dispatcher.sol b/src/base/Dispatcher.sol index 5f7a035..ee8cd78 100755 --- a/src/base/Dispatcher.sol +++ b/src/base/Dispatcher.sol @@ -302,6 +302,7 @@ abstract contract Dispatcher is revert NotAuthorizedForToken(tokenId); } + /// @dev ensure there's follow-up action if v3 position's removed token are sent to router contract (success, output) = address(V3_POSITION_MANAGER).call(inputs); return (success, output); } else if (command == Commands.V4_CL_POSITION_CALL) { diff --git a/src/modules/pancakeswap/StableSwapRouter.sol b/src/modules/pancakeswap/StableSwapRouter.sol index 579e89e..df3e378 100644 --- a/src/modules/pancakeswap/StableSwapRouter.sol +++ b/src/modules/pancakeswap/StableSwapRouter.sol @@ -78,10 +78,11 @@ abstract contract StableSwapRouter is RouterImmutables, Permit2Payments, Ownable } ERC20 tokenOut = ERC20(path[path.length - 1]); + uint256 balanceBefore = tokenOut.balanceOf(address(this)); _stableSwap(path, flag); - uint256 amountOut = tokenOut.balanceOf(address(this)); + uint256 amountOut = tokenOut.balanceOf(address(this)) - balanceBefore; if (amountOut < amountOutMinimum) revert StableTooLittleReceived(); if (recipient != address(this)) pay(address(tokenOut), recipient, amountOut); diff --git a/test/stableSwap/StableSwap.t.sol b/test/stableSwap/StableSwap.t.sol index f480524..ed894a9 100644 --- a/test/stableSwap/StableSwap.t.sol +++ b/test/stableSwap/StableSwap.t.sol @@ -151,10 +151,11 @@ abstract contract StableSwapTest is Test, GasSnapshot { path[0] = token0(); path[1] = token1(); bytes[] memory inputs = new bytes[](1); - inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, 0, path, flag(), true); + inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, 0, path, flag(), false); router.execute(commands, inputs); - assertGt(ERC20(token1()).balanceOf(FROM), BALANCE); + assertEq(ERC20(token0()).balanceOf(FROM), BALANCE); // no token0 taken from user, taken from router + assertGt(ERC20(token1()).balanceOf(FROM), BALANCE); // token1 received } function test_stableSwap_exactInput1For0FromRouter() public { @@ -165,10 +166,28 @@ abstract contract StableSwapTest is Test, GasSnapshot { path[0] = token1(); path[1] = token0(); bytes[] memory inputs = new bytes[](1); - inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, 0, path, flag(), true); + inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, 0, path, flag(), false); router.execute(commands, inputs); - assertGt(ERC20(token0()).balanceOf(FROM), BALANCE); + assertGt(ERC20(token0()).balanceOf(FROM), BALANCE); // token0 received + assertEq(ERC20(token1()).balanceOf(FROM), BALANCE); // no token1 taken from user, taken from router + } + + function test_stableSwap_exactInput0For1_StableTooLittleReceived() public { + // have some AMOUNT * 2 token1 in router, assumed from previous commands + deal(token1(), address(router), AMOUNT * 2); + + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.STABLE_SWAP_EXACT_IN))); + // equivalent: abi.decode(inputs, (address, uint256, uint256, address[], uint256[], bool) + address[] memory path = new address[](2); + path[0] = token0(); + path[1] = token1(); + bytes[] memory inputs = new bytes[](1); + // set minOut as amount * 2 which is not achievable + inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, AMOUNT * 2, path, flag(), true); + + vm.expectRevert(StableSwapRouter.StableTooLittleReceived.selector); + router.execute(commands, inputs); } function test_stableSwap_exactOutput0For1() public { @@ -210,9 +229,10 @@ abstract contract StableSwapTest is Test, GasSnapshot { path[0] = token0(); path[1] = token1(); bytes[] memory inputs = new bytes[](1); - inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, type(uint256).max, path, flag(), true); + inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, type(uint256).max, path, flag(), false); router.execute(commands, inputs); + assertEq(ERC20(token0()).balanceOf(FROM), BALANCE); // no token0 taken from user, taken from router assertGe(ERC20(token1()).balanceOf(FROM), BALANCE + AMOUNT); } @@ -225,10 +245,11 @@ abstract contract StableSwapTest is Test, GasSnapshot { path[0] = token1(); path[1] = token0(); bytes[] memory inputs = new bytes[](1); - inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, type(uint256).max, path, flag(), true); + inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, type(uint256).max, path, flag(), false); router.execute(commands, inputs); assertGe(ERC20(token0()).balanceOf(FROM), BALANCE + AMOUNT); + assertEq(ERC20(token1()).balanceOf(FROM), BALANCE); // no token1 taken from user, taken from router } function token0() internal virtual returns (address);