diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index b9f4d1c..84d686d 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -18,6 +18,7 @@ import {LibSwap} from "../lib/LibSwap.sol"; error TychoRouter__WithdrawalFailed(); error TychoRouter__AddressZero(); error TychoRouter__NegativeSlippage(uint256 amount, uint256 minAmount); +error TychoRouter__AmountInNotFullySpent(uint256 leftoverAmount); error TychoRouter__MessageValueMismatch(uint256 value, uint256 amount); contract TychoRouter is @@ -153,6 +154,7 @@ contract TychoRouter is // For native ETH, assume funds already in our router. Else, transfer and handle approval. if (wrapEth) { _wrapETH(amountIn); + tokenIn = address(_weth); } else if (tokenIn != address(0)) { permit2.permit(msg.sender, permitSingle, signature); permit2.transferFrom( @@ -175,6 +177,11 @@ contract TychoRouter is revert TychoRouter__NegativeSlippage(amountOut, minAmountOut); } + uint256 leftoverAmountIn = IERC20(tokenIn).balanceOf(address(this)); + if (leftoverAmountIn > 0) { + revert TychoRouter__AmountInNotFullySpent(leftoverAmountIn); + } + if (unwrapEth) { _unwrapETH(amountOut); } diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index a842595..af6d925 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -733,4 +733,56 @@ contract TychoRouterTest is TychoRouterTestSetup { // all of it (and thus our splits are correct). assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0); } + + function testSwapAmountInNotFullySpent() public { + // Trade 1 WETH for DAI with 1 swap on Uniswap V2 + // Has invalid data as input! There is only one swap with 60% of the input amount + uint256 amountIn = 1 ether; + deal(WETH_ADDR, ALICE, amountIn); + + vm.startPrank(ALICE); + + ( + IAllowanceTransfer.PermitSingle memory permitSingle, + bytes memory signature + ) = handlePermit2Approval(WETH_ADDR, amountIn); + + bytes memory protocolData = encodeUniswapV2Swap( + WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + ); + + bytes memory swap = encodeSwap( + uint8(0), + uint8(1), + (0xffffff * 60) / 100, // 60% + address(usv2Executor), + bytes4(0), + protocolData + ); + + bytes[] memory swaps = new bytes[](1); + swaps[0] = swap; + + vm.expectRevert( + abi.encodeWithSelector( + TychoRouter__AmountInNotFullySpent.selector, 400000000000000000 + ) + ); + + tychoRouter.swap( + amountIn, + WETH_ADDR, + DAI_ADDR, + 0, + false, + false, + 2, + ALICE, + permitSingle, + signature, + pleEncode(swaps) + ); + + vm.stopPrank(); + } }