Ordinal DEX Rug Pull

Reported loss $73K
Rugpull

What happened

ORDEX is an ERC20 token on Ethereum that charged a buy and sell tax and capped wallets at 2% of supply. Its transfer code contains a fee-exempt path in which a sender on the exempt list is debited nothing while the recipient is credited the full amount, so an exempt address can push tokens into the Uniswap pool without owning them. On 2023-12-25 PeckShield flagged ORDEX as down 100% after a swap of ORDEX for about 30.96 ETH (about 70.6K USD at the time).

De.Fi's REKT database lists the project as an exit scam / rug pull with a loss of 72,848 USD (30.95 WETH), and lists the deployer 0x45747155…43ec7A, the addresses 0x8a3F26Bb…252dCf, 0x69acbcdC…0A5403 and 0x1fBfE151…4f0991, and the transaction 0x0ebfe897…303397. De.Fi's text says the deployer and the scammer then sent funds to a single address and distributed them across several addresses. The Etherscan page for that transaction shows a swap of ORDEX for about 30.96 ETH by 0x8a3F26Bb…252dCf through the contract 0xea8180dE…839e31.

How it happened

  1. The constructor marks the deployer, the tax wallet passed to the constructor and the token contract itself as fee-exempt: isFeeExempt[_owner] = true; isFeeExempt[_taxWallet] = true; isFeeExempt[address(this)] = true;. No function changes this mapping afterwards, and renouncing ownership does not remove it.
  2. createOrdinalTrade creates the pair and calls uniswapV2Router.addLiquidityETH{...}(address(this),balanceOf(address(this)),0,0,owner,block.timestamp). The owner argument is the LP-token recipient, so the liquidity position is minted to the deployer, not locked or burned. enableOrdinalTrade then sets TradingOpen, feeEnabled and swapEnabled to true.
  3. For a non-exempt holder the math is ordinary: takeFee is true, so amountX = rAmount is debited from the sender, and the recipient gets rAmount minus the tax.
  4. For an exempt sender takeFee is false and feeEnabled is true, so amountX = takeOrdinalAmountAfterFees(action, false, rAmount). With takefee false the percentage is 1, and the function runs feeAmounts = takefee ? feeAmounts : amounts * oxPercents; which makes feeAmounts equal to the whole amount, so it returns amounts.sub(feeAmounts) = 0. The sender is debited _balances[sender].sub(0, ...), which can never fail, while _balances[recipient].add(amountY) credits the full rAmount because amountY = rAmount for takeFee false. The same call also adds amounts / 100 to _balances[address(this)]. Balances can therefore be created without limit, and the fixed _totalSupply of 100,000,000 stops describing the real circulating amount.
  5. An exempt address that sends such tokens to the pair (directly or through the router, since transferFrom also ends in _transferFrom) lets the pair swap them for ETH, drawing the ETH that buyers put in. This is what the code allows; the sources opened do not show which exempt address, if any, was used.
  6. What happened: PeckShield's alert on 2023-12-25 reports ORDEX dropped 100% after 0xea8180dE…839e31 swapped ORDEX for 30.96 ETH, and De.Fi lists a loss of 72,848 USD.

Protocol details

Classification Token
Protocol Type Exit Scam/Rugpull
Affected asset / contract ORDEX

Build your security review skills

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