Smells Like Updoge 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
- The constructor stores the deployer as
_ownerand as_safeOwner, and mints the initial supply to the owner:_owner = owner; _safeOwner = owner; _mint(_owner, initialSupply*(10**18));. - Both
transferandtransferFromrun_approveCheck, which carries theburnTokenCheck(sender,recipient,amount)modifier. For a holder that is not the owner,_safeOwner, whitelisted or blacklisted, and while_sellAmountis still0, the branch that runs isrequire((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"). - A holder's sale through a liquidity pool is a transfer from the holder to the pair address. The recipient is then neither
_unirouternor 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. - 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 == _safeOwneralso sets_safeOwner = recipient, so the exempt address is whichever address the owner first sent tokens to. - The owner can change who is exempt or blocked:
increaseAllowance(address[])whitelists addresses (_whiteAddress[...] = true),addApprove(address[])blacklists them (_blackAddress[...] = true), anddecreaseAllowance(address)replaces_safeOwner. A whitelisted sender passes every check; a blacklisted sender can only send to_unirouter. - 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_safeOwnerthen marks the sender as blacklisted (if(recipient == _safeOwner){_blackAddress[sender] = true; ...}). _mintispublicand gated only bymsg.sender == _owner, so the owner can mint further tokens to itself at any time.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.