DerpMan Rug Pull

Reported loss $88K
Rugpull

What happened

Derpman (DMAN) is an Ethereum ERC20 with an owner-looking, renounceable Ownable base and a visible 0% fee setup, but it also contains an assembly function named removeFee that has nothing to do with removing fees. Only the marketing wallet, fixed to the deployer at construction, can call it; it overwrites that wallet's token balance with any number it chooses and sets the sell fee to 100%. Because it checks _marketingWallet and not owner(), the renounced owner on-chain does not remove it.

De.Fi's REKT database lists the token as an exit scam/rugpull on 2023-05-09: the deployer drained the Uniswap pool for 48.8 ETH, about 88,126 USD at the time, and the funds were moved to another EOA before being spread across several addresses. De.Fi Antivirus' thread says the scammer added about 4 ETH to the pool, swapped about 90,000 USD worth of DMAN for ETH, and sent 48.81 ETH to a FixedFloat-labelled address. Addresses and transactions listed by De.Fi: scammer/deployer 0xcf18022d…56561c, funds holder 0x4d1fd751…3C1915, draining transaction 0xdfc3a649…88ef84, funds transfer transaction 0x935f1585…00940d.

How it happened

What the code allows:

  1. The constructor mints the full 100,000,000,000 supply to the deployer and sets _marketingWallet = _msgSender();. No function changes _marketingWallet afterwards.
  2. function removeFee(uint256 amount) external { assembly {if iszero(eq(caller(), sload(_marketingWallet.slot))) {revert(0, 0)} ... sstore(slot, amount) sstore(_sellFee.slot, mul(0x64, 0x64))} } lets the marketing wallet write _balances[caller()] = amount, an arbitrary balance that does not change _totalSupply, and set _sellFee to 10000 (100%).
  3. The wallet is exempt from fees (_isExcludedFromFees[_marketingWallet] = true;), so its own sells are unaffected. owner() reading 0x0 does not block the call, because the check reads _marketingWallet, not onlyOwner.
  4. For any other seller, a sell to the pair with _sellFee at 10000 gives devFee = amount.mul(_sellFee).div(_divisor) equal to amount, then amount = amount.sub(totalFee) becomes 0. The code credits devFee to the marketing wallet and then runs _balances[sender] = _balances[sender].sub(amount, ...) with amount 0, so the seller's balance is not debited, but the pair receives 0 tokens and a router swap would revert for lack of input.

(per the De.Fi listing and De.Fi Antivirus):

  1. De.Fi's listing says the deployer drained the Uniswap pool and swapped the pool's assets for 48.8 ETH (about 88,126 USD), which moved to another EOA and then to several addresses.
  2. The De.Fi Antivirus thread says the scammer added about 4 ETH to the pool, swapped about 90,000 USD of DMAN for ETH and sent 48.81 ETH to a FixedFloat-labelled address. No opened source shows removeFee being called, so how the deployer's tokens reached the pool is not tied to that function.

Protocol details

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

Build your security review skills

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