- Type: Exploit
- Network: Fantom
- Total lost: ~$2.62MM USD
- Category: Bad Data Validation
- Vulnerable contracts:
- Attack transactions:
- Attacker Addresses:
- Attack Block:: 32968740
- Date: Mar 09, 2022
- Reproduce:
forge test --match-contract Exploit_FantasmFinance -vvv
- Call
mint
without providing any backing for your mint - Profit
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);
}
- 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"