Fear NFTs Honeypot

Reported loss Not disclosed
BNB Chain
Honeypot

What happened

De.Fi's REKT database lists Fear NFTs (FEAR) on BNB Smart Chain as an exit scam / honeypot, noting that the owner could disable the transfer function and so restrict users from selling their tokens. The verified source implements this through a burnTokenCheck modifier on every transfer: the owner and a _safeOwner address pass freely, whitelisted addresses pass, and any other sender can send tokens only to the hard-coded _unirouter address unless the transfer amount is below an owner-set _sellAmount, which starts at zero. The owner can also whitelist or blacklist addresses, change _safeOwner, and mint new tokens to itself.

The code does not show whether any of these powers were used on FEAR holders.

How it happened

  1. The constructor sets _owner and _safeOwner to the same address and mints initialSupply*(10**18) to it. The contract has no function that changes _owner.
  2. Every transfer and transferFrom runs through _approveCheck, which carries the modifier burnTokenCheck(sender,recipient,amount).
  3. First branch: if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}. While the two are equal, the first transfer sent by the owner makes its recipient the _safeOwner. The code does not show which address that was.
  4. Privileged senders: if (sender == _owner || sender == _safeOwner || recipient == _owner) lets the transfer run. If the owner sends to itself, _sellAmount = amount records a threshold.
  5. Whitelisted senders (_whiteAddress[sender] == true) also pass. Blacklisted senders hit require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"), so they can only send to _unirouter.
  6. Every other sender falls to if (amount < _sellAmount){ if(recipient == _safeOwner){_blackAddress[sender] = true; ...} _; }else{require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");_;}. _sellAmount is initialised to 0, so until the owner sets a threshold the amount < _sellAmount test is false for every amount, and an ordinary holder's transfer reverts unless the recipient is _unirouter. If a threshold is set, smaller transfers pass, and a transfer to _safeOwner below the threshold succeeds but adds the sender to _blackAddress.
  7. The owner controls who is exempt: increaseAllowance(address[]) whitelists and un-blacklists, addApprove(address[]) blacklists and un-whitelists, decreaseAllowance(address) replaces _safeOwner, and multiTransfer whitelists the first approvecount receivers and gives them a maximum allowance to _unirouter. All are gated by require(msg.sender == _owner, "!owner"). The revert message reads as a balance error, not a restriction.
  8. function _mint(address account, uint256 amount) public is owner-gated and credits _balances[_owner], so the owner can add supply to its own balance after deployment. No transaction showing any of these functions being called was opened.

Protocol details

Classification Token
Protocol Type Exit Scam/Honeypot
Affected asset / contract FEAR

Build your security review skills

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