x20 finance Rug Pull
What happened
x20.finance (X20) was an ERC-20 token on Ethereum (0xd24c65a2…7b13ec) with a sell block hidden in a helper named _ints. The creator, stored as _address0, can call ints(address) to name a second privileged address, _address1. Once that address differs from the creator, any transfer not to or from the creator reverts unless the tokens come from _address1, the Uniswap router or a whitelisted wallet.
On 16 November 2020 the creator added 40,800 X20 and 2 ETH to Uniswap V2 and, 90 seconds later, called ints with the X20/WETH pair as the argument. From then on, buys from the pair went through but holders' sells into it reverted, while the creator could still sell. The creator sold X20 into the pool, and on 17 November removed 282.66 of its 285.66 LP tokens for 5.60 ETH and 14,328 X20.
De.Fi's REKT database lists the case as an exit scam with $2,501 lost.
How it happened
- The constructor records the creator as both privileged addresses and mints the whole supply to it:
_address0 = owner; _address1 = owner; _mint(_address0, initialSupply*(10**18));. On-chain supply is 50,000 X20. - Every transfer runs
_ints(sender, recipient, amount):if(recipient != _address0 && sender != _address0 && _address0!=_address1 && amount > _zero){require(sender == _address1 ||sender==_router || _Addressint[sender], ...)}. While_address1equals the creator the check is skipped, so the token trades normally. ints(address addressn)lets only_address0change_address1. At 11:27:14 UTC on 16 November 2020 the creator added 40,800 X20 and 2 ETH to Uniswap V2. At 11:28:44 UTC it calledints(0xaE8cA64C2A2c2D10D78dB02783081877BBdcBEEF), the X20/WETH pair (transaction 0x1471b466…1f430e).- From then on a buy passes, because the tokens leave from the pair, which is
_address1. A holder's sell fails, because the tokens leave from the holder, who is not_address1, the router or whitelisted. The error message reuses the misleading text "ERC20: transfer from the zero address". - The creator is exempt as
_address0. It sold X20 into the pool, for example 1,200 X20 for 0.433 ETH at 12:11:44 UTC (transaction 0x7917b57c…0a6682). - On 17 November 2020 at 13:18:57 UTC the creator burned 282.66 LP tokens with
removeLiquidityETHWithPermitand received 5.60 ETH and 14,328.11 X20 (transaction 0xe558685b…4c92b6). The payout passes the check because the pair and the router are both allowed senders. The ETH above the creator's 2 ETH deposit had been paid into the pool by buyers.
Protocol details
Evidence
- report CaptainJackAPE post cited by De.Fi as proof twitter.com
- transaction Etherscan transaction 0xff2d791a...a0a6 (initial liquidity) etherscan.io
- transaction Etherscan transaction 0x1471b466...430e (ints call) etherscan.io
- transaction Etherscan transaction 0x7917b57c...6682 (creator sale) etherscan.io
- transaction Etherscan transaction 0xe558685b...92b6 (liquidity removal) etherscan.io
- transaction Etherscan address 0xd24c65a2307266de4f7eeda5c5124e18ed7b13ec (X20 token transactions) etherscan.io
- transaction etherscan.io transaction 0x91ea…2075 etherscan.io
- transaction etherscan.io transaction 0xae12…984a etherscan.io
- transaction etherscan.io transaction 0xc260…a18a etherscan.io
- transaction etherscan.io transaction 0xe622…d855 etherscan.io
- address x20.finance (X20) verified source code on Etherscan etherscan.io
- address Etherscan address 0xaE8cA64C2A2c2D10D78dB02783081877BBdcBEEF (Uniswap V2: X20 13) etherscan.io
- analysis Sourcify match for 0xd24c65a2307266de4f7eeda5c5124e18ed7b13ec on Ethereum repo.sourcify.dev
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.