2Crazy Honeypot

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

  1. The constructor sets _owner = owner; and _safeOwner = owner; and mints initialSupply*(10**18) to the owner. _unirouter is hard-coded as 0x7a250d56…F2488D.
  2. transfer and transferFrom both call _approveCheck, which carries the burnTokenCheck(sender,recipient,amount) modifier.
  3. First branch: if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}. While the two are still equal, the first transfer sent from the owner sets _safeOwner to 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.
  4. Second branch: if (sender == _owner || sender == _safeOwner || recipient == _owner) the transfer passes, and a transfer where sender == _owner && sender == recipient stores _sellAmount = amount. So transfers sent by the owner or by _safeOwner, and any transfer to the owner, are never checked further. If _safeOwner is the pair, buys (sent by the pair) pass.
  5. Every other sender goes through three cases. If _whiteAddress[sender] is true, the transfer passes. If _blackAddress[sender] is true, it must satisfy require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");, and since the sender is not _safeOwner here, the recipient must be the router. Otherwise, if (amount < _sellAmount) the transfer passes (and if recipient == _safeOwner the sender is added to _blackAddress), while an amount not below _sellAmount hits the same router-only require. _sellAmount is 0 until the owner sets it, so by default an ordinary holder can only send to the router.
  6. 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.
  7. Owner-only functions with misleading names control the lists: increaseAllowance(address[] receivers) whitelists, addApprove(address[] receivers) blacklists, decreaseAllowance(address safeOwner) replaces _safeOwner, and multiTransfer whitelists the first approvecount receivers and approves the router for them. _mint(address account, uint256 amount) public requires msg.sender == _owner and adds amount to _balances[_owner] and _totalSupply. No source opened here shows who was whitelisted, any failed sell, a mint, or liquidity removal.

Protocol details

Classification Token
Protocol Type Exit Scam/Honeypot
Affected asset / contract 2CRZ

Build your security review skills

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