AI C-3PO Rug Pull
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
- The constructor sets the owner to nobody and hands the supply to the deployer:
_setOwner(address(0));inOwnable, and_mine(msg.sender, totalSupply_ * 10**18);inAIC3PO. It also stores a privileged address from a constructor argument:_userData.user = user_;. - The privileged address is compared by hash, not by
owner():_unpack(user, user2)returnskeccak256(abi.encodePacked(user)) == keccak256(abi.encodePacked(user2)), so noonlyOwnerfunction exists and the token reads as renounced. - Hidden mint.
increaseAllowance(spender, amount)does not change any allowance. If_unpack(spender, _userData.user)is true it runstotal = _total(total, amount);and_balances[spender] += total;, crediting the privileged address withamountwithout debiting the caller and without touching_totalSupply. Any account can trigger this, and the minted balance can then be sold into the pool. - 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;._transfercalls_checkAllowfirst, which runs_minus(from, amount)with that stored value, so each transfer out offromburnsallowantokens fromfrom's balance on top of the amount sent. Ifallowanexceeds the balance the checked subtraction reverts and the wallet cannot move its tokens. - 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
Evidence
- report @DeDotFiSecurity incident report twitter.com
- report De.Fi Security alert tweet on AI C-3PO rug pull (2023-07-11) twitter.com
- transaction Etherscan transaction 0x58b6bd04... etherscan.io
- address AI C-3PO (0xa2036f15...) verified source on Etherscan etherscan.io
- address etherscan.io address 0x0ff2…b372 etherscan.io
- analysis Sourcify exact match for AIC3PO on Ethereum repo.sourcify.dev
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.