User wrapped tokens get stuck in master router because of incorrect calculation
Swapping exact tokens for ETH swaps underlying token amount, not wrapped token amount and this causes wrapped tokens to get stuck in the contract.\\nIn the protocol the `JalaMasterRouter` is used to swap tokens with less than 18 decimals. It is achieved by wrapping the underlying tokens and interacting with the `JalaRouter02`. Wrapping the token gives it decimals 18 (18 - token.decimals()). There are also functions that swap with native ETH.\\nIn the `swapExactTokensForETH` function the tokens are transferred from the user to the Jala master router, wrapped, approved to `JalaRouter2` and then `IJalaRouter02::swapExactTokensForETH()` is called with the amount of tokens to swap, to address, deadline and path.\\nThe amount of tokens to swap that is passed, is the amount before the wrap. Hence the wrappedAmount - underlyingAmount is stuck.\\nAdd the following test to `JalaMasterRouter.t.sol` and run with `forge test --mt testswapExactTokensForETHStuckTokens -vvv`\\n```\\n    function testswapExactTokensForETHStuckTokens() public {\\n        address wrappedTokenA = IChilizWrapperFactory(wrapperFactory).wrappedTokenFor(address(tokenA));\\n\\n        tokenA.approve(address(wrapperFactory), type(uint256).max);\\n        wrapperFactory.wrap(address(this), address(tokenA), 100);\\n\\n        IERC20(wrappedTokenA).approve(address(router), 100 ether);\\n        router.addLiquidityETH{value: 100 ether}(wrappedTokenA, 100 ether, 0, 0, address(this), type(uint40).max);\\n\\n        address pairAddress = factory.getPair(address(WETH), wrapperFactory.wrappedTokenFor(address(tokenA)));\\n\\n        uint256 pairBalance = JalaPair(pairAddress).balanceOf(address(this));\\n\\n        address[] memory path = new address[](2);\\n        path[0] = wrappedTokenA;\\n        path[1] = address(WETH);\\n\\n        vm.startPrank(user0);\\n        console.log("ETH user balance before:       ", user0.balance);\\n        console.log("TokenA user balance before:    ", tokenA.balanceOf(user0));\\n        console.log("WTokenA router balance before: ", IERC20(wrappedTokenA).balanceOf(address(masterRouter)));\\n\\n        tokenA.approve(address(masterRouter), 550);\\n        masterRouter.swapExactTokensForETH(address(tokenA), 550, 0, path, user0, type(uint40).max);\\n        vm.stopPrank();\\n\\n        console.log("ETH user balance after:       ", user0.balance);\\n        console.log("TokenA user balance after:    ", tokenA.balanceOf(user0));\\n        console.log("WTokenA router balance after: ", IERC20(wrappedTokenA).balanceOf(address(masterRouter)));\\n    }\\n```\\n
In `JalaMasterRouter::swapExactTokensForETH()` multiply the `amountIn` by decimal off set of the token:\\n```\\n    function swapExactTokensForETH(\\n        address originTokenAddress,\\n        uint256 amountIn,\\n        uint256 amountOutMin,\\n        address[] calldata path,\\n        address to,\\n        uint256 deadline\\n    ) external virtual override returns (uint256[] memory amounts) {\\n        address wrappedTokenIn = IChilizWrapperFactory(wrapperFactory).wrappedTokenFor(originTokenAddress);\\n\\n        require(path[0] == wrappedTokenIn, "MS: !path");\\n\\n        TransferHelper.safeTransferFrom(originTokenAddress, msg.sender, address(this), amountIn);\\n        _approveAndWrap(originTokenAddress, amountIn);\\n        IERC20(wrappedTokenIn).approve(router, IERC20(wrappedTokenIn).balanceOf(address(this)));\\n\\n// Add the line below\\n        uint256 decimalOffset = IChilizWrappedERC20(wrappedTokenIn).getDecimalsOffset();\\n// Add the line below\\n        amounts = IJalaRouter02(router).swapExactTokensForETH(amountIn * decimalOffset, amountOutMin, path, to, deadline);\\n// Remove the line below\\n        amounts = IJalaRouter02(router).swapExactTokensForETH(amountIn , amountOutMin, path, to, deadline);\\n    }\\n```\\n
User wrapped tokens get stuck in router contract. The can be stolen by someone performing a `swapExactTokensForTokens()` because it uses the whole balance of the contract when swapping: `IERC20(wrappedTokenIn).balanceOf(address(this))`\\n```\\n        amounts = IJalaRouter02(router).swapExactTokensForTokens(\\n            IERC20(wrappedTokenIn).balanceOf(address(this)),\\n            amountOutMin,\\n            path,\\n            address(this),\\n            deadline\\n        );\\n```\\n
```\\n    function testswapExactTokensForETHStuckTokens() public {\\n        address wrappedTokenA = IChilizWrapperFactory(wrapperFactory).wrappedTokenFor(address(tokenA));\\n\\n        tokenA.approve(address(wrapperFactory), type(uint256).max);\\n        wrapperFactory.wrap(address(this), address(tokenA), 100);\\n\\n        IERC20(wrappedTokenA).approve(address(router), 100 ether);\\n        router.addLiquidityETH{value: 100 ether}(wrappedTokenA, 100 ether, 0, 0, address(this), type(uint40).max);\\n\\n        address pairAddress = factory.getPair(address(WETH), wrapperFactory.wrappedTokenFor(address(tokenA)));\\n\\n        uint256 pairBalance = JalaPair(pairAddress).balanceOf(address(this));\\n\\n        address[] memory path = new address[](2);\\n        path[0] = wrappedTokenA;\\n        path[1] = address(WETH);\\n\\n        vm.startPrank(user0);\\n        console.log("ETH user balance before:       ", user0.balance);\\n        console.log("TokenA user balance before:    ", tokenA.balanceOf(user0));\\n        console.log("WTokenA router balance before: ", IERC20(wrappedTokenA).balanceOf(address(masterRouter)));\\n\\n        tokenA.approve(address(masterRouter), 550);\\n        masterRouter.swapExactTokensForETH(address(tokenA), 550, 0, path, user0, type(uint40).max);\\n        vm.stopPrank();\\n\\n        console.log("ETH user balance after:       ", user0.balance);\\n        console.log("TokenA user balance after:    ", tokenA.balanceOf(user0));\\n        console.log("WTokenA router balance after: ", IERC20(wrappedTokenA).balanceOf(address(masterRouter)));\\n    }\\n```\\n