Skip to content

Latest commit

 

History

History
81 lines (62 loc) · 3.56 KB

064.md

File metadata and controls

81 lines (62 loc) · 3.56 KB

Crazy Jetblack Pigeon

High

The _deposit function in L1StandardERC20Gateway can cause a DoS for standard ERC20 tokens

Summary and Vulnerability Detail

According to the ERC20 standard, the functions name, symbol, and decimals are optional, and no one should expect these functions to be present in a contract.

The _deposit function in the L1StandardERC20Gateway contract calls the name, symbol, and decimals methods to retrieve the name, symbol, and decimals of the ERC20 token. If a valid ERC20 token that does not implement these functions is passed, the call will fail, preventing the user from depositing the tokens.

    function _deposit(
        address _token,
        address _to,
        uint256 _amount,
        bytes memory _data,
        uint256 _gasLimit
    ) internal virtual override nonReentrant {
        require(_amount > 0, "deposit zero amount");

        // 1. Transfer token into this contract.
        address _from;
        (_from, _amount, _data) = _transferERC20In(_token, _amount, _data);

        // 2. Generate message passed to L2StandardERC20Gateway.
        address _l2Token = tokenMapping[_token];
        bytes memory _l2Data;
        if (_l2Token == address(0)) {
            // @note we won't update `tokenMapping` here but update the `tokenMapping` on
            // first successful withdraw. This will prevent user to set arbitrary token
            // metadata by setting a very small `_gasLimit` on the first tx.
            _l2Token = getL2ERC20Address(_token);

            // passing symbol/name/decimal in order to deploy in L2.
            string memory _symbol = IERC20MetadataUpgradeable(_token).symbol();
            string memory _name = IERC20MetadataUpgradeable(_token).name();
            uint8 _decimals = IERC20MetadataUpgradeable(_token).decimals();
            _l2Data = abi.encode(true, abi.encode(_data, abi.encode(_symbol, _name, _decimals)));
        } else {
            _l2Data = abi.encode(false, _data);
        }
        bytes memory _message = abi.encodeCall(
            IL2ERC20Gateway.finalizeDepositERC20,
            (_token, _l2Token, _from, _to, _amount, _l2Data)
        );

        uint256 nonce = IL1CrossDomainMessenger(messenger).messageNonce();
        // 3. Send message to L1CrossDomainMessenger.
        IL1CrossDomainMessenger(messenger).sendMessage{value: msg.value}(counterpart, 0, _message, _gasLimit, _from);

        emit DepositERC20(_token, _l2Token, _from, _to, _amount, _data, nonce);
    }

Impact

The impact is high because deposits will never be possible, as _l2Token == address(0) will always evaluate to true.

Tool used

Manual Review

Recommendation

Below is the right implementation.

        // decimals, symbol & token are not part of the core ERC20 token standard, so we need to support contracts that dont implement them
        (,bytes memory queriedDecimals) = tokenAddress.staticcall(abi.encodeWithSignature("decimals()"));
        (,bytes memory queriedSymbol) = tokenAddress.staticcall(abi.encodeWithSignature("symbol()"));
        (,bytes memory queriedName) = tokenAddress.staticcall(abi.encodeWithSignature("name()"));

        uint8 decimals = abi.decode(queriedDecimals, (uint8));

        string memory symbolString = abi.decode(queriedSymbol, (string));
        string memory nameString = abi.decode(queriedName, (string));

References

https://github.com/sherlock-audit/2024-08-morphl2/blob/main/morph/contracts/contracts/l1/gateways/L1StandardERC20Gateway.sol#L137-L139

https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/bridge/Bridge.sol#L39-L55