Fire Swap Rug Pull

Reported loss Not disclosed
Rugpull

What happened

The FIRE token lets its owner throttle selling. _transfer limits any transfer into an address the owner has flagged in SWAP to MAX tokens (1000 FIRE by default) and then blocks every transfer from that sender for 60 minutes. The owner can change MAX to any value with lit; because transfers must be larger than 1000 wei, a MAX of 1000 wei or less makes every transfer into a flagged address revert.

The owner can also set a transfer fee of up to 10% that is paid to an owner-chosen address. De.Fi's REKT database lists FIRE as an exit scam/rugpull dated 2021-09-03; the code shows these controls, not which of them were used.

How it happened

  1. The owner can flag an address as a swap destination: function MAX1(address _addr, bool _bool) external virtual onlyOwner { SWAP[_addr] = _bool; }. No address is flagged by default.
  2. For any transfer whose recipient is flagged, _transfer runs if(security == true && SWAP[recipient] == true){ require(amount <= MAX, 'MAX SELL 1000 FIRE PER TX'); saveTransData(sender, block.timestamp); }. MAX starts at uint256 private MAX = 1000e18;, which is 1000 FIRE out of a 100000000 FIRE supply minted in the constructor.
  3. The cooldown line is if(TIME = true && timeNow < transData[sender] + 60 minutes){ revert("CAN ONLY MAX 1 FIRE PER 60 MIN"); }. It uses = instead of ==, so Solidity assigns the comparison result to TIME and tests that result; the condition is just timeNow < transData[sender] + 60 minutes, and setTIME cannot switch the cooldown off. After one recorded sale, every transfer from that sender reverts for 60 minutes.
  4. function lit (uint256 TKP) external virtual onlyOwner { MAX = TKP; } has no bounds. _transfer also requires amount > 1000, so with MAX at 1000 or lower no amount satisfies both checks and every transfer into a flagged address reverts while security is true. setSEC(bool) toggles security.
  5. Fees: uint256 fee = amount.mul(txFee).div(1000); with require(_newTxFee <= 100, "fee too big"); in setFee, so the cap is 10% although the comment says capped to 1%. The sender is debited the full amount and the recipient gets amount.sub(fee); the fee is credited only if(transferToFeeDistributorAmount > 0 && feeDistributor != address(0)), so with the default zero feeDistributor the fee portion is credited to nobody.
  6. The code has no mint function after the constructor (_mint(msg.sender, 100000000e18); is the only call), so these controls limit and tax selling rather than inflate supply.

Protocol details

Classification Exchange (DEX)
Protocol Type Exit Scam/Rugpull
Affected asset / contract FIRE
Protocol links Website

Build your security review skills

Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.