LinkPool 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
- Both
transferandtransferFromrun_approveCheck, which carries the modifierburnTokenCheck(sender,recipient,amount). The modifier has two top-level branches. First: if_owner == _safeOwnerand the sender is_owner, the transfer passes and_safeOwner = recipient;is executed. Because_safeOwneris initially_owner, the first transfer sent by_ownermoves_safeOwnerto that recipient. An owner self-transfer in this state only sets_safeOwnerto the owner again and does not touch_sellAmount. - Otherwise (so
_owner != _safeOwner, or the sender is not the owner),_owner,_safeOwnerand anyone sending to_ownerpass. Inside this branch,if (sender == _owner && sender == recipient){_sellAmount = amount;}sets_sellAmount. It is therefore only set when_ownersends tokens to itself after_safeOwnerhas been changed to another address. - Any other sender who is in
_whiteAddresspasses with no further check. - A sender in
_blackAddress(and not whitelisted) is checked first:require((sender == _safeOwner)||(recipient == _unirouter), ...). This sender is never_safeOwnerhere, since_safeOwnerpassed in step 2, so the only open recipient is_unirouter(0x7a250d56…F2488D) (sending to_owneralso passed in step 2). A blacklisted sender never reaches the_sellAmounttest, so a small amount does not help it. - A sender in neither list passes when
amount < _sellAmount._sellAmountis 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. Ifamount >= _sellAmount, the call must satisfyrecipient == _unirouteror it reverts. - The owner-only functions
increaseAllowance(whitelist, un-blacklist),addApprove(blacklist, un-whitelist),decreaseAllowance(sets_safeOwner) andmultiTransfer(whitelists the firstapprovecountreceivers and approves_unirouterfor the maximum amount on them) are named like ERC-20 allowance helpers but change these access lists._mintispublicand owner-only, and addsamountto_balances[_owner]while the emittedTransferevent names theaccountargument.
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
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.