Home - Coinspect Security

Fantasm Finance

Total Losses

$2.6M+

Date

Network

fantom logo fantom

Categories

business logic data validation

Step-by-step

  1. Call mint without providing any backing for your mint
  2. Profit

Detailed Description

As most tokens, you can mint Fantasm on some conditions. Particularly, Fantasm wanted to ask for some native tokens _ftmIn and some amount of an extra token _fantasmIn to mint some XFTM.

So, in short, you need to give FTM (native token) and FXM (non-native, is burned) to mint some XFTM.

The problem is that the mint function never checks for the amount of FMT deposited, allowing the attacker to mint with only FXM.

    function mint(uint256 _fantasmIn, uint256 _minXftmOut) external payable nonReentrant {
        require(!mintPaused, "Pool::mint: Minting is paused");
        uint256 _ftmIn = msg.value;
        address _minter = msg.sender;

        // This is  supposed to mint. There are three parameters:
        // 1. Native token passed `_ftmIn`
        // 2. _fantasmIn an amount
        // 3.`_minXftmOut` slippage protection
        // What you say here is "Giving you _ftmIn native, I want at least minXftmOut, and I will put _fantasmIn as collateral"

        (uint256 _xftmOut, , uint256 _minFantasmIn, uint256 _ftmFee) = calcMint(_ftmIn, _fantasmIn);
        require(_minXftmOut <= _xftmOut, "Pool::mint: slippage");
        require(_minFantasmIn <= _fantasmIn, "Pool::mint: Not enough Fantasm input");
        require(maxXftmSupply >= xftm.totalSupply() + _xftmOut, "Pool::mint: > Xftm supply limit");

        WethUtils.wrap(_ftmIn);
        userInfo[_minter].lastAction = block.number;

        if (_xftmOut > 0) {
            userInfo[_minter].xftmBalance = userInfo[_minter].xftmBalance + _xftmOut;
            unclaimedXftm = unclaimedXftm + _xftmOut;
        }

        if (_minFantasmIn > 0) {
            fantasm.safeTransferFrom(_minter, address(this), _minFantasmIn);
            fantasm.burn(_minFantasmIn);
        }

        if (_ftmFee > 0) {
            WethUtils.transfer(feeReserve, _ftmFee);
        }

        emit Mint(_minter, _xftmOut, _ftmIn, _fantasmIn, _ftmFee);
    }

Possible mitigations

The obvious recommendation here is “check that counterpart token is received”, but this can be covered by a test. Make sure to have negative testing as part of the suite of your contract, with tests that check that “should not mint XFTM without backing native token”