Plankton Plankton Rug Pull
What happened
Plankton Plankton (PLANK) is an ERC-20 token deployed on Ethereum on 4 August 2023 at 0x7a1abb5b…1b37ba, with 42.2 billion tokens credited to the deployer 0x7bAc42Fd…c2d70e. Its constructor sets owner() to the zero address, so it looks renounced, but it also stores a hidden address, 0x49968e2b…4274cb, that can attach a hidden surcharge to any holder through a function named Approve (capital A); every transfer by that holder then tries to move the extra amount, and a surcharge larger than the balance makes all its transfers revert. On 6 August 2023 at 04:59 UTC the deployer withdrew its Uniswap V3 liquidity position in transaction 0x0fa4f9d1…e9e09e, receiving about 155.28 ETH and 15.26 billion PLANK.
Cyvers reported the removal as 157 ETH (about $287K), with 30 ETH deposited to Tornado Cash and the rest sent to CoinTool via 0x85e6e67e…c1f8c0; De.Fi's REKT database puts the loss at $288,021 and cites Tornado Cash deposit 0xcb3cdeae…300534 and CoinTool transfer 0x1c287c72…079705.
How it happened
- The
Ownableconstructor calls_setOwner(address(0)), soowner()is the zero address from deployment and noonlyOwnerfunction can ever run. ThePlankconstructor credits the full supply tomsg.senderbut emitsTransfer(address(0), owner(), _totalSupply), so the mint event names the zero address instead of the deployer who holds the tokens. - The constructor stores the
dex_argument as a hidden role,_dex = dex_; Sourcify's constructor arguments show it is 0x49968e2b…4274cb. Approve(address account, uint256 amount)is public. When the caller is_dex,_requireAllowsets_fed[account] = amountfor any chosen holder.- On every transfer,
_transferreadssec(sender)(which returns_fed) and adds it to the amount:amount += saylor. A flagged holder therefore moves the requested amount plus the surcharge, and if the surcharge exceeds its balance the subtraction reverts withERC20: transfer amount exceeds balance, blocking sells. - The trap is not airtight: when any other address calls
Approve, the else branch runs_fed[account] = totalwithtotalstill 0, so anyone can clear any holder's surcharge, though the hidden address can set it again. - The loss came from liquidity, not the token code: in transaction 0x0fa4f9d1…e9e09e the deployer called
multicallon the Uniswap V3 position manager and withdrew about 155.28 ETH and 15.26 billion PLANK from the pool.
Protocol details
Evidence
- report @cyversalerts incident report twitter.com
- report Cyvers Alerts: deployer of $PLANK has removed 157 $ETH twitter.com
- transaction PLANK contract creation transaction etherscan.io
- transaction PLANK liquidity removal transaction 0x0fa4f9d1...e09e etherscan.io
- transaction etherscan.io transaction 0x1c28…9705 etherscan.io
- transaction etherscan.io transaction 0xcb3c…0534 etherscan.io
- address Plank (Plankton Plankton) verified source code etherscan.io
- address etherscan.io address 0x7bAc…d70e etherscan.io
- address etherscan.io address 0x85e6…f8c0 etherscan.io
- analysis Sourcify match for 0x7a1abb5b9baab8d971a318800dd43c8fb91b37ba on Ethereum repo.sourcify.dev
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.