2Crazy Honeypot
What happened
2Crazy (2CRZ) is an ERC20 token on Ethereum, deployed on 2021-06-20 as contract 0x519395b0…ea9e70. De.Fi's REKT database lists it as an exit scam / honeypot in which the owner could stop holders from selling. The verified source routes every transfer through a modifier, burnTokenCheck, that lets the owner and one privileged address (_safeOwner) move tokens freely, lets whitelisted holders move tokens freely, and otherwise allows ordinary holders to send only to the hard-coded Uniswap V2 router address.
A holder selling into a pair that is _safeOwner therefore reverts unless the owner has whitelisted them. The owner can also mint to itself and change the privileged address.
How it happened
- The constructor sets
_owner = owner;and_safeOwner = owner;and mintsinitialSupply*(10**18)to the owner._unirouteris hard-coded as 0x7a250d56…F2488D. transferandtransferFromboth call_approveCheck, which carries theburnTokenCheck(sender,recipient,amount)modifier.- First branch:
if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}. While the two are still equal, the first transfer sent from the owner sets_safeOwnerto its recipient. If that transfer is the one that seeds the liquidity pair, the pair becomes_safeOwner. The code does not show which transaction set it on-chain. - Second branch:
if (sender == _owner || sender == _safeOwner || recipient == _owner)the transfer passes, and a transfer wheresender == _owner && sender == recipientstores_sellAmount = amount. So transfers sent by the owner or by_safeOwner, and any transfer to the owner, are never checked further. If_safeOwneris the pair, buys (sent by the pair) pass. - Every other sender goes through three cases. If
_whiteAddress[sender]is true, the transfer passes. If_blackAddress[sender]is true, it must satisfyrequire((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");, and since the sender is not_safeOwnerhere, the recipient must be the router. Otherwise,if (amount < _sellAmount)the transfer passes (and ifrecipient == _safeOwnerthe sender is added to_blackAddress), while an amount not below_sellAmounthits the same router-onlyrequire._sellAmountis 0 until the owner sets it, so by default an ordinary holder can only send to the router. - A sell through the router calls
transferFrom(holder, pair, amount). With the pair as_safeOwner, an unlisted holder's recipient is the pair, not_unirouter, so the call reverts with "ERC20: transfer amount exceeds balance" even though the balance is sufficient. Whitelisted holders and the owner are not affected. - Owner-only functions with misleading names control the lists:
increaseAllowance(address[] receivers)whitelists,addApprove(address[] receivers)blacklists,decreaseAllowance(address safeOwner)replaces_safeOwner, andmultiTransferwhitelists the firstapprovecountreceivers and approves the router for them._mint(address account, uint256 amount) publicrequiresmsg.sender == _ownerand addsamountto_balances[_owner]and_totalSupply. No source opened here shows who was whitelisted, any failed sell, a mint, or liquidity removal.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.