Jewels Rug Pull
What happened
Jewels (JWL) is an ERC20 token on Ethereum with a Uniswap V2 pool against WETH. PeckShield reported on 24 Nov 2023 that the address 0x3d6b0693…bD6522 swapped 1,000,000,000,000 JWL for about 31 WETH (about 64.3K USD), a quantity a thousand times the 1,000,000,000 JWL total supply. De.Fi's REKT database lists the loss as 63,794 USD (31.14 WETH) and an exit scam / rugpull, and lists the deployer as 0x537A494f…a90011, the funds holder as of Nov 27, 2023 as 0x1607644E…d4C29B, and a Binance withdrawal in transaction 0xcc5ceda2…622db2. De.Fi's listing also says the funds went to another EOA and then to Binance through several addresses, and that the deployer sent 2.13 ETH to the scammer's address a couple of minutes after the swap.
The code explains how a quantity above the supply can be sold. In _transfer, a sale to the pair from the hard-coded fee wallet revWallet sets taxOn = true, and later if(taxOn) amount -= amount; zeroes the amount that is debited from the seller, while the pair and the token contract are still credited. That wallet can therefore push tokens into the pool without holding them, and sell them for the pool's WETH.
How it happened
- The constructor hard-codes
revWallet = payable(0xd1F28C4BE394729e248C90fc2ef6d5e8Ff3D4A4f);and sets_exFromMax[revWallet] = true;. No function in the contract changes_exFromMax, andrevWalletis not in_exFromFees, so it is taxed like an ordinary seller. - When tokens are sent to the pair (
to == uniswapV2Pair),_transferrunsif (_exFromMax[from]) taxOn = true;and then sets_tax = taxFeeSell;. The constructor setstaxFeeSell = 24andsetFeesallows the owner to change it. - In the tax branch the code computes
uint256 taxTokens = (amount * _tax) / 100;anduint256 transferAmount = amount - taxTokens;, then runsif(taxOn) amount -= amount;, soamountbecomes 0. The line_balance[from] -= amount;therefore subtracts nothing fromrevWallet, while_balance[to] += transferAmount;and_balance[address(this)] += taxTokens;still credit the pair and the token contract. Nothing checks the seller's balance against the original amount, so tokens are created in the pool from nothing. FortransferFromthe only limit is the allowance, which the holder of the allowance can set to any value withapprove. - A sale from
revWalletthrough the Uniswap router withswapExactTokensForETHSupportingFeeOnTransferTokensthen pays out WETH from the pool against tokens that were never debited from anyone. An ordinary holder selling to the pair does not hittaxOn, so their balance is debited normally. - What happened: Etherscan shows transaction 0xfaf6c9d4…092e3b, sent by 0x3d6b0693…bD6522 to 0xd1F28C4B…3D4A4f (the
revWalletaddress) via asave(uint256 amount)call, with token transfers of 970 billion JWL to the Uniswap V2 pool, 30 billion JWL to the token contract (a 3% tax on 1 trillion), and about 31.10 WETH out of the pool. PeckShield reported the same sale as 1,000,000,000,000 JWL for 31 WETH.
Protocol details
Evidence
- report PeckShieldAlert post on Jewels JWL twitter.com
- report JWL source on Sourcify repo.sourcify.dev
- transaction Etherscan transaction 0xfaf6c9d4...2e3b etherscan.io
- transaction etherscan.io transaction 0xcc5c…2db2 etherscan.io
- address JWL contract source on Etherscan (verified) etherscan.io
- address etherscan.io address 0x1607…C29B etherscan.io
- address etherscan.io address 0x3d6b…6522 etherscan.io
- address etherscan.io address 0x537A…0011 etherscan.io
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.