code4rena-2023-12-shell-protocol-l03

[L-03] User can escape fees on ERC1155 tokens

보고서

Summary

수수료 계산 divider보다 작은 양을 ERC1155 unwrap 할 시 수수료를 내지 않을 수 있다.

Keyword

arithmetic error, rounding error, precision, token decimals

Vulnerability

외부 ERC1155 역시 decimal이 다를 수 있지만, ERC20과는 다르게 decimal 18로 정규화되지 않았다. Ocean 토큰은 해당 ERC1155 토큰의 decimal과 동일한 decimal을 사용하게 된다.

function _erc1155Wrap(
        address tokenAddress,
        uint256 tokenId,
        uint256 amount,
        address userAddress,
        uint256 oceanId
    )
        private
    {
        //@audit Note that there is no `_convertDecimals()` call
        if (tokenAddress == address(this)) revert NO_RECURSIVE_WRAPS();
        _ERC1155InteractionStatus = INTERACTION;
@>      IERC1155(tokenAddress).safeTransferFrom(userAddress, address(this), tokenId, amount, "");
        _ERC1155InteractionStatus = NOT_INTERACTION;
        emit Erc1155Wrap(tokenAddress, tokenId, amount, userAddress, oceanId);
    }

수수료를 계산하는 _calculateUnwrapFee 함수에서, unwrapFeeDivisor는 최소값이 2000이므로, 사용자가 unwarp하는 금액을 2000 미만으로 할 수 있다면 수수료를 지불할 필요가 없다. 따라서 사용자가 2 decimal의 ERC1155 100개의 토큰(즉, 100e2)을 wrap 했다면, 100e2 의 토큰을 한 번에 unwarp하는 대신 5번에 걸쳐 erc1155Unwrap을 호출하여 수수료를 지불하지 않을 수 있다. 100e2의 토큰을 언랩하고 수수료로 5를 지불하는 대신, 2000씩 5번 unwrap 한다.

    function _calculateUnwrapFee(uint256 unwrapAmount) private view returns (uint256 feeCharged) {
@>      feeCharged = unwrapAmount / unwrapFeeDivisor;
    }

Impact

ERC1155 unwrap 할 시 수수료를 내지 않을 수 있다.

Mitigation

ERC1155 도 18 decimal로 정규화한다.


tags: bughunting, shell protocol, smart contract, solidity, rounding error, arithmetic error, severity low, token decimals