LinkPool Honeypot

Reported loss Not disclosed
Ethereum
Honeypot

What happened

LinkPool (LPL) on Ethereum overrides its ERC-20 transfer logic with a burnTokenCheck modifier. For ordinary holders, a transfer succeeds if the recipient is the hard-coded _unirouter address, or the holder is whitelisted, or the holder is not blacklisted and the amount is below _sellAmount, which starts at zero. A blacklisted holder can only send to _unirouter (or to _owner).

Everything else reverts with "ERC20: transfer amount exceeds balance". The owner controls the whitelist, the blacklist, _sellAmount and _safeOwner. De.Fi's REKT database lists the token as an Exit Scam/Honeypot dated 2021-03-26, with the note that the owner could disable the transfer function and restrict users from selling.

How it happened

  1. Both transfer and transferFrom run _approveCheck, which carries the modifier burnTokenCheck(sender,recipient,amount). The modifier has two top-level branches. First: if _owner == _safeOwner and the sender is _owner, the transfer passes and _safeOwner = recipient; is executed. Because _safeOwner is initially _owner, the first transfer sent by _owner moves _safeOwner to that recipient. An owner self-transfer in this state only sets _safeOwner to the owner again and does not touch _sellAmount.
  2. Otherwise (so _owner != _safeOwner, or the sender is not the owner), _owner, _safeOwner and anyone sending to _owner pass. Inside this branch, if (sender == _owner && sender == recipient){_sellAmount = amount;} sets _sellAmount. It is therefore only set when _owner sends tokens to itself after _safeOwner has been changed to another address.
  3. Any other sender who is in _whiteAddress passes with no further check.
  4. A sender in _blackAddress (and not whitelisted) is checked first: require((sender == _safeOwner)||(recipient == _unirouter), ...). This sender is never _safeOwner here, since _safeOwner passed in step 2, so the only open recipient is _unirouter (0x7a250d56…F2488D) (sending to _owner also passed in step 2). A blacklisted sender never reaches the _sellAmount test, so a small amount does not help it.
  5. A sender in neither list passes when amount < _sellAmount. _sellAmount is initialised to 0, so that is never true until the owner has set it as in step 2. If that small transfer goes to _safeOwner, the sender is moved to the blacklist (_blackAddress[sender] = true; _whiteAddress[sender] = false;) and the transfer still goes through, so that sender's next transfer faces the blacklist rule in step 4. If amount >= _sellAmount, the call must satisfy recipient == _unirouter or it reverts.
  6. The owner-only functions increaseAllowance (whitelist, un-blacklist), addApprove (blacklist, un-whitelist), decreaseAllowance (sets _safeOwner) and multiTransfer (whitelists the first approvecount receivers and approves _unirouter for the maximum amount on them) are named like ERC-20 allowance helpers but change these access lists. _mint is public and owner-only, and adds amount to _balances[_owner] while the emitted Transfer event names the account argument.

The code allows all of the above; no transaction was opened, so which addresses were whitelisted or blacklisted, what _safeOwner or _sellAmount were set to, and whether holders were blocked is not established here.

Protocol details

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

Build your security review skills

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