AI C-3PO Rug Pull

Reported loss $150K
Rugpull

What happened

AI C-3PO (C3PO) is an Ethereum token (contract 0xa2036f15…823ce5) deployed on 2023-07-11 by 0x0ff2aeb5…feb372, which received the whole supply. The contract looks ownerless because its Ownable constructor sets the owner to the zero address, but a separate address stored in _userData.user (a constructor argument) has hidden powers: anyone can call increaseAllowance to credit that address with any amount of tokens that were never minted, and that address can call Approve to make every transfer out of a chosen wallet burn extra tokens from the sender's balance. De.Fi's alert on 2023-07-11 (19:57 UTC) reported that the deployer drained the liquidity and took 78 ETH, about $147,500, sending it on to a wallet starting 0x714; the live listing records about $150,000 in WETH spread across four wallets.

The removal transaction listed is 0x58b6bd04…e03fa9, sent by the deployer to Uniswap's position manager. The source shows what the hidden functions allow; it does not show that either was used.

How it happened

  1. The constructor sets the owner to nobody and hands the supply to the deployer: _setOwner(address(0)); in Ownable, and _mine(msg.sender, totalSupply_ * 10**18); in AIC3PO. It also stores a privileged address from a constructor argument: _userData.user = user_;.
  2. The privileged address is compared by hash, not by owner(): _unpack(user, user2) returns keccak256(abi.encodePacked(user)) == keccak256(abi.encodePacked(user2)), so no onlyOwner function exists and the token reads as renounced.
  3. Hidden mint. increaseAllowance(spender, amount) does not change any allowance. If _unpack(spender, _userData.user) is true it runs total = _total(total, amount); and _balances[spender] += total;, crediting the privileged address with amount without debiting the caller and without touching _totalSupply. Any account can trigger this, and the minted balance can then be sold into the pool.
  4. Hidden per-wallet penalty. Approve(address from, uint256 amount) only takes effect when the caller is the privileged address (requireAllow), and it stores _userDatas[from].allowan = amount;. _transfer calls _checkAllow first, which runs _minus(from, amount) with that stored value, so each transfer out of from burns allowan tokens from from's balance on top of the amount sent. If allowan exceeds the balance the checked subtraction reverts and the wallet cannot move its tokens.
  5. What was reported: De.Fi says the deployer removed the liquidity and took 78 ETH (about $147,500). Etherscan shows the removal transaction going from the deployer to the Uniswap V3 positions contract. Liquidity removal is not controlled by this token's code, and no opened transaction shows the hidden mint or the penalty being used.

Protocol details

Classification Token
Protocol Type Exit Scam/Rugpull
Affected asset / contract AI C-3PO
Protocol links @aic3p0

Build your security review skills

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