TubbyCoin Honeypot

Reported loss Not disclosed
Ethereum
Honeypot

What happened

De.Fi's REKT database lists TubbyCoin (TUBBY) on Ethereum as an exit scam/honeypot and states that the contract owner could disable the transfer function, which restricted users in selling their tokens. In the verified source, every transfer runs through the burnTokenCheck modifier, which reverts for an ordinary holder unless the destination is the owner or the address stored in _unirouter. Because _sellAmount starts at 0, the only route for a holder to sell into a pool is to be whitelisted by the owner.

The owner also controls the whitelist, the blacklist, _safeOwner, _sellAmount, and an owner-only _mint. The code does not show which of these the owner used.

How it happened

  1. Each transfer, transfer and transferFrom, calls _approveCheck, which is wrapped in burnTokenCheck(sender,recipient,amount). sender here is the address the tokens leave, so the checks apply to the holder, not to the router calling on their behalf.
  2. Privileged paths pass: if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;} (the first transfer out of the owner while _safeOwner is still the owner makes the recipient _safeOwner), and if (sender == _owner || sender == _safeOwner || recipient == _owner), so anyone can send tokens to the owner. A self-transfer by the owner also executes _sellAmount = amount;.
  3. A sender with _whiteAddress[sender] == true is let through unconditionally.
  4. A sender with _blackAddress[sender] == true must satisfy require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"). Since _safeOwner already passed in step 2, in practice a blacklisted holder can only send to _unirouter (0x7a250d56…F2488D).
  5. A sender on neither list reaches if (amount < _sellAmount). _sellAmount is initialised to 0, so amount < 0 is false and the else branch applies the same require as step 4: only transfers to _unirouter succeed, and a transfer to any other recipient, such as a pool, reverts. Once the owner has set _sellAmount, a smaller transfer to _safeOwner succeeds and runs _blackAddress[sender] = true; _whiteAddress[sender] = false;, so the sender is blacklisted from then on.
  6. Owner-only functions control the lists: multiTransfer whitelists the first approvecount receivers and sets a maximum allowance for _unirouter on their balances, increaseAllowance whitelists and un-blacklists a list of addresses, addApprove blacklists and un-whitelists them, and decreaseAllowance(address safeOwner) replaces _safeOwner.
  7. _mint is public with require(msg.sender == _owner, ...) and adds amount to _balances[_owner], so the owner can increase the supply.

Protocol details

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

Build your security review skills

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