Polkasol Honeypot

Reported loss Not disclosed
Ethereum
Honeypot

What happened

Polkasol (PSOL, Ethereum contract 0x165569b8…dff11a) wraps every transfer in a burnTokenCheck modifier that lets ordinary holders send tokens only to one hard-coded router address, _unirouter. A holder's transfer to any other address, such as a pair in a router sell, reverts with ERC20: transfer amount exceeds balance unless the owner has whitelisted the holder or has set a _sellAmount threshold above the amount. The owner can whitelist, blacklist, repoint _safeOwner and set _sellAmount, and can mint to itself.

De.Fi's REKT database lists Polkasol as an Exit Scam/Honeypot and states that the contract owner could disable the transfer function, which restricted users in selling their tokens.

How it happened

  1. The constructor credits supply to the owner. _mint always runs _balances[_owner] = _balances[_owner].add(amount); but emits Transfer(address(0), account, amount) for the account argument, and the constructor calls it 20 times: once for _owner, nine times for 0x529c5a51…85DE61 and ten times for 0x51e029a5…9AaAeC. The Transfer events therefore name those two addresses while the balance is credited to _owner. _mint is public and checks msg.sender == _owner, so the owner can mint again later.
  2. Every transfer and transferFrom runs _approveCheck, which carries burnTokenCheck. While _owner == _safeOwner, the owner's first outgoing transfer runs _safeOwner = recipient;, so _safeOwner becomes whichever address receives that transfer. The owner can also reset it with decreaseAllowance(address safeOwner).
  3. For senders that are not _owner or _safeOwner and are not whitelisted, with _sellAmount at its initial 0 the check reduces to require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"). A holder can therefore send tokens to _unirouter, or to _owner (the recipient == _owner branch passes), but a sell that moves tokens to a pair via transferFrom(holder, pair, amount) reverts because the recipient is neither.
  4. Whether buys work depends on the address that sends the tokens, which is the pair; the code does not show whether the pair became _safeOwner, so this is not asserted.
  5. The owner has switches: increaseAllowance(address[] receivers) sets _whiteAddress true and _blackAddress false (whitelisted senders skip the check); addApprove does the reverse; multiTransfer can send tokens and, for the first approvecount receivers, whitelist them and approve _unirouter for the maximum amount. A blacklisted sender faces the same router-only rule as an unlisted one.
  6. A self-transfer by the owner (sender == _owner && sender == recipient) sets _sellAmount = amount. After that, a non-whitelisted, non-blacklisted sender can transfer any amount < _sellAmount to any recipient, and if the recipient is _safeOwner the sender is also marked _blackAddress[sender] = true.
  7. The code shows what the owner can do; no transaction was opened showing these calls, liquidity removal, or the size of any loss.

Protocol details

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

Build your security review skills

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