Assessment reports>Y2K Finance>High findings>The lack of token addresses' verification
Category: Coding Mistakes

The lack of token addresses' verification

High Severity
High Impact
Low Likelihood

Description

The permitSwapAndBridge and swapAndBridge functions allow users to perform the swap, and after that, bridge the resulting tokens to another chain using Stargate, which is a decentralised bridge and exchange building on top of the Layer Zero protocol.

These functions have the following parameters: swapPayload, receivedToken, and _srcPoolId. The swapPayload parameter contains all the necessary data for the swap, including the path array with the list of tokens involved in the swap process. It is assumed that the final address of the token participating in the swap will be used for cross-chain swap.

The receivedToken token address will be used by the _bridge function for assigning the approval for the stargateRouter contract.

Besides that, the user controls the _srcPoolId parameter, which determines the pool address, which is associated with a specific token and will hold the assets of the tokens that will be transferred from the current contact inside stargateRouter. However, there is no verification that these three addresses -- receivedToken, the last address in path and pool.token() -- match each other.

When using native tokens, the user should pass the wethAddress address as receivedToken because before _bridge, the necessary amount of tokens should be withdrawn from the weth contract. After that, the receivedToken will be rewritten to zero address. Currently there is no verification that the receivedToken is not zero initially.

function swapAndBridge(
    uint amountIn,
    address fromToken,
    address receivedToken,
    uint16 srcPoolId,
    uint16 dstPoolId,
    bytes1 dexId,
    bytes calldata swapPayload,
    bytes calldata bridgePayload
) external payable {
    _checkConditions(amountIn);

    ERC20(fromToken).safeTransferFrom(msg.sender, address(this), amountIn);

    uint256 receivedAmount;
    if (dexId != 0x05) {
        receivedAmount = _swap(dexId, amountIn, swapPayload);
    } else {
        ERC20(fromToken).safeApprove(balancerVault, amountIn);
        receivedAmount = _swapBalancer(swapPayload);
    }

    if (receivedToken == wethAddress) {
        WETH(wethAddress).withdraw(receivedAmount);
        receivedToken = address(0);
    }

    _bridge(
        receivedAmount,
        receivedToken,
        srcPoolId,
        dstPoolId,
        bridgePayload
    );
}

function _bridge(
    uint amountIn,
    address fromToken,
    uint16 srcPoolId,
    uint16 dstPoolId,
    bytes calldata payload
) private {
    if (fromToken == address(0)) {
/*  NOTE: If sending after swap to ETH then msg.value will be < amountIn as it only contains the fee
    If sending without swap msg.value will be > amountIn as it contains both fee + amountIn
**/
        uint256 msgValue = msg.value > amountIn
            ? msg.value
            : amountIn + msg.value;
    IStargateRouter(stargateRouterEth).swapETHAndCall{value: msgValue}(...);
    ...
    }
...
}

Impact

Due to the lack of verification that the receivedToken address matches the last address in the path array and pool.token() address, users are able to employ any token address as the receivedToken. This potentially allows them to successfully execute cross-chain swaps using tokens owned by the contract.

In instances where a user initially sets the receivedToken address to the zero address, the required amount of tokens will not be withdrawn from the weth contract. Consequently, the contract will attempt to transfer to the stargateRouter contract the funds present in its balance before the transaction took place.

In both scenarios, if the contract possesses any tokens, they can be utilized instead of the tokens received during the execution of the swap.

This also leads to a problem in the _bridge function during msgValue calculation. When the fromToken (receivedToken from swapAndBridge) is not the outcome of a swap, users can specify any amountIn as the result of a swap involving a different token. This amountIn value will then be used as the ETH value. Consequently, if the contract holds other funds, they will be sent to stargateRouterEth along with the user's fee.

Recommendations

We recommend to add the check that the receivedToken and the last address in path and pool.token() match each other --- and also that the receivedToken address is not equal to zero address.

Remediation

This issue has been acknowledged by Y2K Finance, and a fix was implemented in commit 5f79149.

Zellic © 2024Back to top ↑