Mother of All Ponzis Rug Pull

Reported loss $1K
Ethereum
Rugpull

What happened

Mother of All Ponzis (MOAP) is an ERC-20 whose _transfer path is replaced by a burnTokenCheck modifier. By default, anyone other than the owner, a whitelisted address or the pool can only send tokens to the Uniswap router address, so a sale through the router, which pulls tokens from the holder into the pair, reverts with a fake transfer amount exceeds balance message. The owner alone controls the whitelist, the blacklist, the recorded pool address and an unlimited _mint.

Per Etherscan, the deployer address 0x6a0Fe346…5360e8 added 158 ETH and 7,372 MOAP to Uniswap V2 on 2020-12-22 (tx 0x1eacfa0f…d2a246) and removed 173.74 ETH and 6,708.04 MOAP of liquidity on 2020-12-23 (tx 0xbb5e7942…f60e09). De.Fi's REKT database lists the incident as an Exit Scam/Rugpull with a 1,336 USD loss.

How it happened

  1. The constructor sets _owner = owner; _safeOwner = owner; and mints the initial supply to the owner. There is no transferOwnership or renounceOwnership, so _owner never changes.
  2. Every transfer and transferFrom runs through _approveCheck, which carries the burnTokenCheck(sender,recipient,amount) modifier. While _owner == _safeOwner, the first transfer sent by the owner executes _safeOwner = recipient;, so the first recipient of the owner's tokens, for example a pool receiving the liquidity deposit, becomes _safeOwner.
  3. In that modifier, transfers pass freely when sender == _owner || sender == _safeOwner || recipient == _owner, and when _whiteAddress[sender] == true. Sends from the pool (buys) therefore pass if the pool is _safeOwner.
  4. Every other sender falls to if (amount < _sellAmount){...}else{require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");_;}. _sellAmount starts at 0, so amount < _sellAmount is false and the require runs. A router sale calls transferFrom(holder, pair, amount): the recipient is the pair, not _unirouter, so it reverts for a non-whitelisted holder.
  5. The owner can change who is trapped: increaseAllowance(receivers) whitelists and un-blacklists, addApprove(receivers) blacklists, multiTransfer whitelists the first approvecount receivers and gives them a maximum allowance to the router, and decreaseAllowance(safeOwner) overwrites _safeOwner. An owner self-transfer sets _sellAmount = amount; after that, a non-listed holder selling less than that amount to _safeOwner succeeds but is blacklisted by _blackAddress[sender] = true;.
  6. _mint(address account, uint256 amount) is public and restricted only by require(msg.sender == _owner, ...), so the owner can also create more supply.
  7. What happened, per Etherscan: the deployer address added 158 ETH and 7,372 MOAP to Uniswap V2 on 2020-12-22 and removed 173.74 ETH and 6,708.04 MOAP on 2020-12-23, about 14.5 hours later.

Protocol details

Classification Token
Protocol Type Exit Scam/Rugpull
Affected asset / contract MOAP

Build your security review skills

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