HungryWorldBSC Honeypot

Reported loss Not disclosed
BNB Chain
Honeypot

What happened

HungryWorldBSC (HGW) routes every transfer and transferFrom through a modifier called burnTokenCheck. For a holder who is not on the owner's whitelist, the modifier only lets tokens go to the hard-coded _unirouter address or to _owner; a transfer to any other recipient reverts with the message "ERC20: transfer amount exceeds balance" even when the balance is sufficient. The owner can whitelist or blacklist addresses, move the privileged _safeOwner address and mint to itself.

De.Fi's REKT database lists HGW on BNB Smart Chain as an Exit Scam/Honeypot and says the contract owner could disable the transfer function, which restricted users in selling their tokens. The code shows what the owner can do; no transaction was opened to show which addresses were listed.

How it happened

  1. The constructor sets _owner = owner; and _safeOwner = owner; and mints initialSupply*(10**18) to _owner; the on-chain supply read is 5,000,000,000 tokens. _owner is assigned only there, so the contract has no function to transfer or renounce it.
  2. transfer and transferFrom both call _approveCheck, which is declared internal burnTokenCheck(sender,recipient,amount). The check runs before any balance is debited, so a blocked transfer reverts without moving tokens.
  3. The first transfer sent by the owner re-points the privileged address: if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}. After that, transfers where the sender is _owner or _safeOwner, or where the recipient is _owner, pass: if (sender == _owner || sender == _safeOwner || recipient == _owner){.
  4. Every other sender is checked in this order. A sender with _whiteAddress[sender] == true passes. A sender with _blackAddress[sender] == true must satisfy require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"), which for a non-privileged sender means the recipient is _unirouter.
  5. A sender on neither list reaches if (amount < _sellAmount){. _sellAmount starts at 0, so that test is false for every amount and the sender falls to the same require: only a transfer to _unirouter succeeds. A sale that delivers tokens to any other address, for example a liquidity pair, reverts. A buy sends tokens out of the pair, so it passes only if the pair is whitelisted or is _safeOwner; the source does not show either.
  6. _sellAmount is set when the owner sends tokens to itself: if (sender == _owner && sender == recipient){_sellAmount = amount;}. Once it is set, a non-listed holder can move amounts below it, and doing so to _safeOwner flags the sender: if(recipient == _safeOwner){_blackAddress[sender] = true; _whiteAddress[sender] = false;}.
  7. Owner-only functions manage the lists. increaseAllowance(address[] memory receivers) sets _whiteAddress to true and _blackAddress to false, addApprove does the opposite, decreaseAllowance(address safeOwner) replaces _safeOwner, and multiTransfer whitelists its first approvecount receivers and grants each a maximum allowance to _unirouter. The names do not describe what these functions do.
  8. _mint(address account, uint256 amount) is public but requires msg.sender == _owner, and it adds the amount to _balances[_owner]. The owner can therefore increase its own balance at any time; the source alone does not show that it did.

Protocol details

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

Build your security review skills

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