Memecoin Rug Pull
What happened
A fake MEME token on Ethereum (contract 0xFac762a5…11a690) that copied the name, symbol and links of the real Memecoin project. Its verified source hides owner-only functions behind misleading names: one function mints an unrecorded token balance to the contract itself and sells it into the Uniswap MEME/WETH pair for 99.999% of the pair's WETH reserve, another blocks chosen addresses from ever sending tokens, and others emit Transfer and Swap events that no real trade backs (one of them also pulls tokens from an address into the pair with a real transferFrom, and another rewrites listed balances). Aegis reports that on 2023-10-26 memecoin-deployer.eth dumped 564,231,958,802,870 MEME, completely depleting the pool and taking about 105.27 ETH (about 188.7K USD); that token count is roughly 8,000 times the 69,000,000,000 MEME total supply, which fits a balance created outside the supply rather than tokens held by buyers.
The De.Fi listing records the loss as 188,095 USD (105.27 WETH), with the proceeds sent to scammer address 0x4649F627…9f9527 in the same transaction (0xd7e09b71…b44e1d), then moved to another address, and 98 ETH distributed to 50 addresses through Dispers.app (transaction 0x2f8fda5d…71d4cb).
How it happened
- The constructor creates the Uniswap V2 pair and gives the whole supply to the deployer, who is also the owner:
owner = msg.sender; ... _totalSupply = 69_000_000_000e18; b[msg.sender] = _totalSupply;. The contract has no function that transfers or renounces ownership, so the owner role stays with the deployer address. - Code allows: the owner-only
Address(address _r)computes 99.999% of the pair's WETH reserve,uint256 calling = (Sub(_RR.WETH()) * 99999) / 100000;, asks the router how many MEME that would cost withgetAmountsIn, then credits that amount to the token contract's own balance with no change to_totalSupply:b[ ... address(uint160(uint256(_T())>>96)) ... ] += value[0];(_T()isbytes32(uint256(uint160(address(this))) << 96), so the index resolves toaddress(this)). - It then approves the router for that balance and sells it:
a[address(this)][address(_RR)] = b[address(this)];followed by_RR.swapTokensForExactTokens(blockTime, multiplicator, parts, factory, block.timestamp + 1200), where the output recipientfactoryis really the_rargument the owner passed in. The result is that the WETH leaves the pair to an address of the owner's choice in one call, backed by tokens that did not exist in the supply. - Code allows:
multicall(bytes32[] data, uint256 _p)is owner-only and, for each address packed indata[i]>>96, runsCheckAmount, which emits aTransferevent from that address toaddress(_pair)for its balance (no balance moves) and setsc[address] = FetchToken(rS), whereFetchTokenreturns_a + 10, so the value is never zero._transferthen checksif (c[from] > 0){ require(add(c[from], b[from]) == 0); }, which cannot hold for a non-zeroc, so every transfer whosefromis that address reverts. If the address is a holder, the pair cannot take their tokens, so they cannot sell; if it is the pair, buyers cannot receive tokens. Nothing in the code resetsc. - Code allows, with real and fake effects mixed:
multicall2runsCheckAmount2, which emits the same kind ofTransferevent toaddress(_pair)and then really overwrites the listed address's balance withFetchToken2(rS), which equalsb[address] / _p.Executeloops over owner-supplied addresses and callscallUniswap, whose first line,IERC20(unmount).transferFrom(router, address(_pair), cycleWidth);, is a realtransferFromon a token contract the owner names, moving tokens from the supplied address into the pair (it succeeds only if that address approved this contract). It then emitsTransfer(address(_pair), router, transfer)andSwap(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D, ...), events that no swap in the pair backs. Block explorers and trackers read these events as real trades. Whether any of these functions were called is not shown by an opened source. - What happened, per Aegis and the De.Fi listing: on 2023-10-26 the deployer dumped 564,231,958,802,870 MEME and completely depleted the pool, receiving about 105.27 ETH; the funds were sent to the scammer address in the same transaction and later split across 50 addresses. Which owner function the transaction called was not checked against the transaction itself, so the link between step 2-3 and this event is an inference from the matching size of the dump and the single-transaction pool depletion.
Protocol details
Evidence
- report Aegis Rugpull Alert: Memecoin $MEME on Ethereum (via fxtwitter) twitter.com
- transaction etherscan.io transaction 0x2f8f…d4cb etherscan.io
- transaction etherscan.io transaction 0xd7e0…4e1d etherscan.io
- address Memecoin.sol verified source on Etherscan (local Sourcify copy, 00_Memecoin.sol) etherscan.io
- address etherscan.io address 0x4649…9527 etherscan.io
- address etherscan.io address 0x8697…9bf5 etherscan.io
- analysis Sourcify repository entry for the fake MEME contract repo.sourcify.dev
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.