BitSpawn Token Honeypot
What happened
BitSpawn Token (SPWN) is an Ethereum ERC-20 whose transfers pass through a modifier called burnTokenCheck. The owner and one privileged address (_safeOwner) can move tokens freely, but an ordinary holder who is not whitelisted can only send tokens to the hard-coded Uniswap V2 router address, so a sell that delivers tokens to the pair reverts with "ERC20: transfer amount exceeds balance" even though the holder's balance is sufficient. The owner can also whitelist or blacklist addresses, set a sell-size limit, and call _mint.
De.Fi's REKT database lists the token as an Exit Scam/Honeypot dated 2021-06-17 with no loss amount.
How it happened
- The constructor sets
_owner = owner; _safeOwner = owner;and mints the initial supply to the owner. - In
burnTokenCheck, the first transfer sent by the owner while_owner == _safeOwnerruns_safeOwner = recipient;. If that first transfer is the liquidity deposit, the pool address becomes_safeOwner; the source alone does not show which address was captured. - Transfers from
_owneror_safeOwner, or to_owner, always pass:if (sender == _owner || sender == _safeOwner || recipient == _owner). A buy is a transfer from the pool, so it passes when the pool is_safeOwner. - Every other sender falls to the final branch. A whitelisted sender (
_whiteAddress[sender] == true) passes. A blacklisted sender, and a sender in neither list while the sell limit is still its default_sellAmount = 0, must satisfyrequire((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");. Since_unirouteris hard-coded to 0x7a250d56…F2488D, an ordinary holder's sell, where the router callstransferFrom(holder, pair, amount)with the pair as recipient, reverts, and so does a wallet-to-wallet transfer. - The owner controls who escapes the filter.
increaseAllowance(address[] receivers)sets_whiteAddressto true and clears the blacklist,addApprove(address[] receivers)does the reverse,decreaseAllowance(address safeOwner)reassigns_safeOwner, andmultiTransferwhitelists the firstapprovecountreceivers and sets their router allowance to the maximum. The function names do not describe what they do. - If the owner sends an amount to itself (
sender == _owner && sender == recipient),_sellAmount = amount;and a non-listed holder may then sell any amount strictly below that value:if (amount < _sellAmount). When the recipient is_safeOwner, that sell goes through but the sender is blacklisted by_blackAddress[sender] = true;, so it can then only send to the router. _mintispublicand checks onlymsg.sender == _owner, so the owner can mint more tokens to itself at any time; its constructor use is the only mint visible in the source.- What the code allows and what happened are separate: no transaction was opened showing the owner calling any of these functions, who held the pool's LP tokens, or whether liquidity was removed.
Protocol details
Evidence
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.