LuckyDoge Honeypot

Reported loss Not disclosed
BNB Chain
Honeypot

What happened

LuckyDoge (LDE) is a BNB Smart Chain token whose transfer modifier, burnTokenCheck, lets only the owner, one owner-chosen address _safeOwner, and whitelisted addresses move tokens freely. With the default _sellAmount of 0, any other holder can send only to the hard-coded _unirouter address or to the owner; a transfer to any other address, such as a liquidity pair, reverts with "ERC20: transfer amount exceeds balance" even though the balance is untouched. The owner can whitelist or blacklist addresses, replace _safeOwner, and mint more tokens.

De.Fi's REKT database lists the token as an exit scam/honeypot and says the owner could disable the transfer function.

How it happened

  1. The constructor sets _owner = owner; _safeOwner = owner; and mints the initial supply to the owner. No function in the contract changes _owner afterwards.
  2. Every transfer and transferFrom runs burnTokenCheck. Its first branch is if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}. The first time the owner sends tokens, _safeOwner becomes that recipient, and the transfer passes.
  3. Next branch: if (sender == _owner || sender == _safeOwner || recipient == _owner) passes. Owner, _safeOwner and anyone sending to the owner are never blocked. Whitelisted senders (_whiteAddress[sender] == true) also pass.
  4. Blacklisted senders must satisfy require((sender == _safeOwner)||(recipient == _unirouter), ...), so they can send only to _unirouter.
  5. Every other holder falls to if (amount < _sellAmount){ ... _; }else{require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");_;}. _sellAmount starts at 0, so amount < _sellAmount is never true and the require runs. A sell sends tokens from the holder to a pair, which is neither the owner nor _unirouter, so it reverts. Wallet-to-wallet transfers revert the same way. The revert comes before the balance lines, so the holder keeps the tokens. Transfers whose sender is _safeOwner pass, so if _safeOwner is the pair, buys are not stopped; the code does not say which address _safeOwner holds.
  6. The owner controls who is in each class: increaseAllowance(address[]) whitelists, addApprove(address[]) blacklists, decreaseAllowance(address) replaces _safeOwner, and multiTransfer can send tokens and whitelist the first approvecount receivers while giving _unirouter an unlimited allowance over them. Once _safeOwner != _owner, an owner self-transfer sets _sellAmount = amount. Below that amount other holders can send anywhere, and a send to _safeOwner also blacklists the sender.
  7. _mint(address account, uint256 amount) is public but requires msg.sender == _owner, and it credits _balances[_owner] (not account) while emitting a Transfer event to account, so the owner can increase the supply. No opened transaction shows any of these functions being called or sells failing, so these are what the code allows.

Protocol details

Classification Token
Protocol Type Exit Scam/Honeypot
Affected asset / contract LDE

Build your security review skills

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