Polkasol 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
- The constructor credits supply to the owner.
_mintalways runs_balances[_owner] = _balances[_owner].add(amount);but emitsTransfer(address(0), account, amount)for theaccountargument, 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._mintispublicand checksmsg.sender == _owner, so the owner can mint again later. - Every
transferandtransferFromruns_approveCheck, which carriesburnTokenCheck. While_owner == _safeOwner, the owner's first outgoing transfer runs_safeOwner = recipient;, so_safeOwnerbecomes whichever address receives that transfer. The owner can also reset it withdecreaseAllowance(address safeOwner). - For senders that are not
_owneror_safeOwnerand are not whitelisted, with_sellAmountat its initial 0 the check reduces torequire((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"). A holder can therefore send tokens to_unirouter, or to_owner(therecipient == _ownerbranch passes), but a sell that moves tokens to a pair viatransferFrom(holder, pair, amount)reverts because the recipient is neither. - 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. - The owner has switches:
increaseAllowance(address[] receivers)sets_whiteAddresstrue and_blackAddressfalse (whitelisted senders skip the check);addApprovedoes the reverse;multiTransfercan send tokens and, for the firstapprovecountreceivers, whitelist them and approve_unirouterfor the maximum amount. A blacklisted sender faces the same router-only rule as an unlisted one. - A self-transfer by the owner (
sender == _owner && sender == recipient) sets_sellAmount = amount. After that, a non-whitelisted, non-blacklisted sender can transfer anyamount < _sellAmountto any recipient, and if the recipient is_safeOwnerthe sender is also marked_blackAddress[sender] = true. - 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
Evidence
- report @Token_Sniffer incident report twitter.com
- address Etherscan verified source, Polkasol 0x165569b8cb75c44e2c884a5aa51f427153dff11a etherscan.io
- analysis Sourcify exact match, chain 1, Polkasol repo.sourcify.dev
- analysis De.Fi REKT database listing for Polkasol (text as supplied in record metadata) de.fi
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.