BitSpawn Token Honeypot

Reported loss Not disclosed
Ethereum
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

  1. The constructor sets _owner = owner; _safeOwner = owner; and mints the initial supply to the owner.
  2. In burnTokenCheck, the first transfer sent by the owner while _owner == _safeOwner runs _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.
  3. Transfers from _owner or _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.
  4. 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 satisfy require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");. Since _unirouter is hard-coded to 0x7a250d56…F2488D, an ordinary holder's sell, where the router calls transferFrom(holder, pair, amount) with the pair as recipient, reverts, and so does a wallet-to-wallet transfer.
  5. The owner controls who escapes the filter. increaseAllowance(address[] receivers) sets _whiteAddress to true and clears the blacklist, addApprove(address[] receivers) does the reverse, decreaseAllowance(address safeOwner) reassigns _safeOwner, and multiTransfer whitelists the first approvecount receivers and sets their router allowance to the maximum. The function names do not describe what they do.
  6. 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.
  7. _mint is public and checks only msg.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.
  8. 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

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

Build your security review skills

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