Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

yotov721 - User wrapped tokens get stuck in master router because of incorrect calculation #146

Open
sherlock-admin opened this issue Mar 5, 2024 · 4 comments
Labels
Has Duplicates A valid issue with 1+ other issues describing the same vulnerability Medium A valid Medium severity issue Reward A payout will be made for this issue Sponsor Confirmed The sponsor acknowledged this issue is valid Will Fix The sponsor confirmed this issue will be fixed

Comments

@sherlock-admin
Copy link
Contributor

sherlock-admin commented Mar 5, 2024

yotov721

high

User wrapped tokens get stuck in master router because of incorrect calculation

Summary

Swapping exact tokens for ETH swaps underlying token amount, not wrapped token amount and this causes wrapped tokens to get stuck in the contract.

Vulnerability Detail

In 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.

In 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.

The amount of tokens to swap that is passed, is the amount before the wrap. Hence the wrappedAmount - underlyingAmount is stuck.

Add the following test to JalaMasterRouter.t.sol and run with forge test --mt testswapExactTokensForETHStuckTokens -vvv

    function testswapExactTokensForETHStuckTokens() public {
        address wrappedTokenA = IChilizWrapperFactory(wrapperFactory).wrappedTokenFor(address(tokenA));

        tokenA.approve(address(wrapperFactory), type(uint256).max);
        wrapperFactory.wrap(address(this), address(tokenA), 100);

        IERC20(wrappedTokenA).approve(address(router), 100 ether);
        router.addLiquidityETH{value: 100 ether}(wrappedTokenA, 100 ether, 0, 0, address(this), type(uint40).max);

        address pairAddress = factory.getPair(address(WETH), wrapperFactory.wrappedTokenFor(address(tokenA)));

        uint256 pairBalance = JalaPair(pairAddress).balanceOf(address(this));

        address[] memory path = new address[](2);
        path[0] = wrappedTokenA;
        path[1] = address(WETH);

        vm.startPrank(user0);
        console.log("ETH user balance before:       ", user0.balance);
        console.log("TokenA user balance before:    ", tokenA.balanceOf(user0));
        console.log("WTokenA router balance before: ", IERC20(wrappedTokenA).balanceOf(address(masterRouter)));

        tokenA.approve(address(masterRouter), 550);
        masterRouter.swapExactTokensForETH(address(tokenA), 550, 0, path, user0, type(uint40).max);
        vm.stopPrank();

        console.log("ETH user balance after:       ", user0.balance);
        console.log("TokenA user balance after:    ", tokenA.balanceOf(user0));
        console.log("WTokenA router balance after: ", IERC20(wrappedTokenA).balanceOf(address(masterRouter)));
    }

Impact

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))

        amounts = IJalaRouter02(router).swapExactTokensForTokens(
            IERC20(wrappedTokenIn).balanceOf(address(this)),
            amountOutMin,
            path,
            address(this),
            deadline
        );

Code Snippet

https://github.com/sherlock-audit/2024-02-jala-swap/blob/main/jalaswap-dex-contract/contracts/JalaMasterRouter.sol#L284-L301

Tool used

Manual Review, foundry

Recommendation

In JalaMasterRouter::swapExactTokensForETH() multiply the amountIn by decimal off set of the token:

    function swapExactTokensForETH(
        address originTokenAddress,
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override returns (uint256[] memory amounts) {
        address wrappedTokenIn = IChilizWrapperFactory(wrapperFactory).wrappedTokenFor(originTokenAddress);

        require(path[0] == wrappedTokenIn, "MS: !path");

        TransferHelper.safeTransferFrom(originTokenAddress, msg.sender, address(this), amountIn);
        _approveAndWrap(originTokenAddress, amountIn);
        IERC20(wrappedTokenIn).approve(router, IERC20(wrappedTokenIn).balanceOf(address(this)));

+        uint256 decimalOffset = IChilizWrappedERC20(wrappedTokenIn).getDecimalsOffset();
+        amounts = IJalaRouter02(router).swapExactTokensForETH(amountIn * decimalOffset, amountOutMin, path, to, deadline);
-        amounts = IJalaRouter02(router).swapExactTokensForETH(amountIn , amountOutMin, path, to, deadline);
    }
@github-actions github-actions bot added High A valid High severity issue Has Duplicates A valid issue with 1+ other issues describing the same vulnerability labels Mar 9, 2024
@sherlock-admin2 sherlock-admin2 added Sponsor Confirmed The sponsor acknowledged this issue is valid Will Fix The sponsor confirmed this issue will be fixed labels Mar 11, 2024
@nevillehuang nevillehuang added Medium A valid Medium severity issue and removed High A valid High severity issue labels Mar 13, 2024
@nevillehuang
Copy link
Collaborator

nevillehuang commented Mar 13, 2024

If a sufficient slippage (which is users responsibility) is set, this will at most cause a revert, so medium severity is more appropriate. (The PoC set slippage to zero)

@sherlock-admin4
Copy link

The protocol team fixed this issue in PR/commit https://github.com/jalaswap/jalaswap-dex-contract/commit/9ed6e8f4f6ad762ef7b747ca2d367f6cfd78973e.

@sherlock-admin4 sherlock-admin4 changed the title Scrawny Cyan Orca - User wrapped tokens get stuck in master router because of incorrect calculation yotov721 - User wrapped tokens get stuck in master router because of incorrect calculation Mar 18, 2024
@sherlock-admin4 sherlock-admin4 added the Reward A payout will be made for this issue label Mar 18, 2024
@spacegliderrrr
Copy link
Collaborator

fix looks good, right amount is now passed to the router

@sherlock-admin4
Copy link

The Lead Senior Watson signed off on the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Has Duplicates A valid issue with 1+ other issues describing the same vulnerability Medium A valid Medium severity issue Reward A payout will be made for this issue Sponsor Confirmed The sponsor acknowledged this issue is valid Will Fix The sponsor confirmed this issue will be fixed
Projects
None yet
Development

No branches or pull requests

5 participants