Clover.finance Honeypot
What happened
This Ethereum token was deployed under the name Clover.finance (CLV). It hides a sell gate inside a modifier named burnTokenCheck that runs on every transfer and transferFrom. The owner and one privileged address (_safeOwner) can always move tokens, but any other holder's transfer reverts with a fake "transfer amount exceeds balance" error unless the owner has whitelisted them, the amount is below an owner-set threshold, or the tokens go straight to the Uniswap V2 router.
A normal Uniswap sell sends tokens to the pair, not the router, so it fails. The owner can whitelist, blacklist, change _safeOwner and mint without limit through functions given misleading ERC-20 names, and the contract has no way to give up these powers. De.Fi's REKT database lists this CLV contract as an exit scam/honeypot; no loss figure or transaction was found.
How it happened
- The first time the owner sends tokens, the recipient becomes
_safeOwner:if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}. If that first transfer is the liquidity deposit, the Uniswap pair becomes_safeOwner, so buys (transfers out of the pair) always pass. - Transfers from the owner or
_safeOwner, or to the owner, always pass. An owner self-transfer sets the sell threshold:if (sender == _owner && sender == recipient){_sellAmount = amount;}._sellAmountstarts at 0. - Any other sender who is not whitelisted and sends at least
_sellAmounthitsrequire((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");. A Uniswap V2 sell callstransferFrom(holder, pair, amount), so the recipient is the pair, and the sell reverts with a misleading balance error. - If the owner sets a threshold, a holder can send a smaller amount, but sending it to
_safeOwnermarks them:if(recipient == _safeOwner){_blackAddress[sender] = true; _whiteAddress[sender] = false;}. Blacklisted senders then face the same router-onlyrequirefor every amount. - The owner controls the lists through misnamed functions:
increaseAllowance(address[])whitelists,addApprove(address[])blacklists,decreaseAllowance(address safeOwner)replaces_safeOwner, andmultiTransferwhitelists recipients and approves the router for them. _mintis public and gated only byrequire(msg.sender == _owner, ...), with no cap, and it credits the owner:_balances[_owner] = _balances[_owner].add(amount);. The contract has no function to change or renounce_owner.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.