Fire Swap Rug Pull
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
- 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. - For any transfer whose recipient is flagged,
_transferrunsif(security == true && SWAP[recipient] == true){ require(amount <= MAX, 'MAX SELL 1000 FIRE PER TX'); saveTransData(sender, block.timestamp); }.MAXstarts atuint256 private MAX = 1000e18;, which is 1000 FIRE out of a 100000000 FIRE supply minted in the constructor. - 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 toTIMEand tests that result; the condition is justtimeNow < transData[sender] + 60 minutes, andsetTIMEcannot switch the cooldown off. After one recorded sale, every transfer from that sender reverts for 60 minutes. function lit (uint256 TKP) external virtual onlyOwner { MAX = TKP; }has no bounds._transferalso requiresamount > 1000, so withMAXat 1000 or lower no amount satisfies both checks and every transfer into a flagged address reverts whilesecurityis true.setSEC(bool)togglessecurity.- Fees:
uint256 fee = amount.mul(txFee).div(1000);withrequire(_newTxFee <= 100, "fee too big");insetFee, so the cap is 10% although the comment sayscapped to 1%. The sender is debited the fullamountand the recipient getsamount.sub(fee); the fee is credited onlyif(transferToFeeDistributorAmount > 0 && feeDistributor != address(0)), so with the default zerofeeDistributorthe fee portion is credited to nobody. - 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
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.