Smells Like Updoge Honeypot

Reported loss Not disclosed
Ethereum
Honeypot

What happened

Smells Like Updoge (UPDOGE) is an Ethereum token, deployed from 0xCaB42da4…349b3D in transaction 0x7c81da5d…ae0950 on 2021-05-20. De.Fi's REKT database lists it as an Exit Scam/Honeypot, saying the contract owner could disable the transfer function and so stop users from selling. The verified source shows how the token can trap holders: the burnTokenCheck modifier on transfer and transferFrom lets a non-privileged holder send tokens only to the hard-coded router address 0x7a250d56…F2488D (or to the owner), and a sale through a pool sends tokens to the pair, so it reverts with a misleading transfer amount exceeds balance error.

The owner-only functions can whitelist or blacklist addresses, change the exempt _safeOwner address and mint more tokens to the owner. Whether the owner used any of these powers against holders is not shown by the source; the listing is the source for the honeypot label.

How it happened

  1. The constructor stores the deployer as _owner and as _safeOwner, and mints the initial supply to the owner: _owner = owner; _safeOwner = owner; _mint(_owner, initialSupply*(10**18));.
  2. Both transfer and transferFrom run _approveCheck, which carries the burnTokenCheck(sender,recipient,amount) modifier. For a holder that is not the owner, _safeOwner, whitelisted or blacklisted, and while _sellAmount is still 0, the branch that runs is require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance").
  3. A holder's sale through a liquidity pool is a transfer from the holder to the pair address. The recipient is then neither _unirouter nor the sender _safeOwner, so the call reverts. The holder's balance is not debited, because the revert happens before _balances[sender] is reduced, and the error text claims a balance problem that does not exist.
  4. Transfers from the owner or _safeOwner, and transfers to _owner, skip the check: if (sender == _owner || sender == _safeOwner || recipient == _owner). The first transfer by the owner while _owner == _safeOwner also sets _safeOwner = recipient, so the exempt address is whichever address the owner first sent tokens to.
  5. The owner can change who is exempt or blocked: increaseAllowance(address[]) whitelists addresses (_whiteAddress[...] = true), addApprove(address[]) blacklists them (_blackAddress[...] = true), and decreaseAllowance(address) replaces _safeOwner. A whitelisted sender passes every check; a blacklisted sender can only send to _unirouter.
  6. The owner can also self-transfer to set _sellAmount (if (sender == _owner && sender == recipient){_sellAmount = amount;}). Once it is set, a non-privileged holder can send amounts below it, but a transfer to _safeOwner then marks the sender as blacklisted (if(recipient == _safeOwner){_blackAddress[sender] = true; ...}).
  7. _mint is public and gated only by msg.sender == _owner, so the owner can mint further tokens to itself at any time.

Protocol details

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

Build your security review skills

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