Croco Inu Honeypot
What happened
Croco Inu ($CROC) is an Ethereum token whose transfer and transferFrom calls run through a burnTokenCheck modifier. A holder who is not the owner, not _safeOwner and not on the owner's whitelist can move tokens only to the hard-coded router address (_unirouter) or to the owner, unless the owner has set a _sellAmount threshold, in which case smaller transfers pass to any recipient. Any other transfer reverts with "ERC20: transfer amount exceeds balance".
The owner alone can whitelist or blacklist addresses, repoint _safeOwner and set _sellAmount. De.Fi's REKT database lists the token as an Exit Scam/Honeypot and describes it as a contract whose owner could disable the transfer function, which restricted users in selling their tokens.
How it happened
What the code allows:
- The constructor sets
_owner = owner;and_safeOwner = owner;, so the owner starts as both the admin and the privileged address._sellAmountstarts at0. transferandtransferFromboth call_approveCheck, which is wrapped in theburnTokenCheckmodifier. While_owner == _safeOwner, the first branchif (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}lets any owner-sent transfer through and moves_safeOwnerto its recipient. The code does not show who that recipient is.- The second branch
if (sender == _owner || sender == _safeOwner || recipient == _owner){lets the owner, the current_safeOwner, and anyone sending to the owner transfer without further checks. Inside it,if (sender == _owner && sender == recipient){_sellAmount = amount;}lets the owner set_sellAmountwith a self-transfer. That line is reachable only once_owner != _safeOwner, because while they are equal the first branch fires instead. - Any other sender whose
_whiteAddress[sender]is true passes to any recipient. - A sender whose
_blackAddress[sender]is true reachesrequire((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"). Because_safeOwnersenders were already handled in step 3, this passes only when the recipient is_unirouter(0x7a250d56…F2488D); a transfer to the owner passes through step 3 instead. - A sender on neither list is tested with
if (amount < _sellAmount). If it is true, the transfer passes to any recipient, andif(recipient == _safeOwner){_blackAddress[sender] = true; _whiteAddress[sender] = false;}blacklists the sender when the recipient is_safeOwner. Otherwise the samerequire((sender == _safeOwner)||(recipient == _unirouter), ...)applies. While_sellAmountis0,amount < _sellAmountis never true, so such a sender can transfer only to_unirouteror to the owner. - The owner controls the lists:
increaseAllowance(address[] receivers)sets_whiteAddress[...] = trueand_blackAddress[...] = false,addApprove(address[] receivers)does the reverse,decreaseAllowance(address safeOwner)replaces_safeOwner, andmultiTransferwhitelists the firstapprovecountrecipients. All are gated byrequire(msg.sender == _owner, "!owner"). _mint(address account, uint256 amount)ispublicand owner-only, and adds to_balances[_owner]with no cap, so the owner can create more supply.
De.Fi's REKT database lists the token as an Exit Scam/Honeypot where holders were restricted from selling. The supplied sources do not show which of the owner functions were called on this deployment, or any failed sale.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.