Plankton Plankton Rug Pull

Reported loss $288K
Rugpull

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

  1. The Ownable constructor calls _setOwner(address(0)), so owner() is the zero address from deployment and no onlyOwner function can ever run. The Plank constructor credits the full supply to msg.sender but emits Transfer(address(0), owner(), _totalSupply), so the mint event names the zero address instead of the deployer who holds the tokens.
  2. The constructor stores the dex_ argument as a hidden role, _dex = dex_; Sourcify's constructor arguments show it is 0x49968e2b…4274cb.
  3. Approve(address account, uint256 amount) is public. When the caller is _dex, _requireAllow sets _fed[account] = amount for any chosen holder.
  4. On every transfer, _transfer reads sec(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 with ERC20: transfer amount exceeds balance, blocking sells.
  5. The trap is not airtight: when any other address calls Approve, the else branch runs _fed[account] = total with total still 0, so anyone can clear any holder's surcharge, though the hidden address can set it again.
  6. The loss came from liquidity, not the token code: in transaction 0x0fa4f9d1…e9e09e the deployer called multicall on the Uniswap V3 position manager and withdrew about 155.28 ETH and 15.26 billion PLANK from the pool.

Protocol details

Classification Token
Protocol Type Exit Scam/Rugpull
Affected asset / contract PLANK
Protocol links Website @AAAHHM_SOL

Build your security review skills

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