ROACH RACE Rug Pull

Reported loss $160K
Rugpull

What happened

ROACH RACE (ROACH) is an Ethereum ERC-20 whose real control sits with a separate mst address fixed in the constructor, not with the owner. That address can blacklist or greylist wallets, whitelist wallets so they skip every check, and switch on an automatic greylist mode in which any wallet that receives tokens from a non-listed source can send them out only within a short block window. The contract also exposes unrestricted functions that emit fake Transfer, Swap and Approval events without changing balances. De.Fi's listing and a De.Fi tech lead's post report that the deployer removed 88.1 WETH (about 160,000 USD) from the Uniswap V2 pool about a day after launch, and the listing says the funds were spread across four wallets. The token code does not contain the liquidity withdrawal itself.

Contract Creator: 0x6007d8d7…531c2a

Liquidity Removal tx: 0x1aaf5bd1…07fac2

How it happened

What the code allows:

  1. Owner powers are minimal: the owner can only call renounceOwnership. All admin functions use onlyMst (require(msg.sender == mst);), where mst=_mst; is set once in the constructor and there is no function that changes or renounces it. owner reads as the zero address in an eth_call on 2026-09-30, but that does not affect the mst role, and no source opened shows when it was renounced.
  2. Every transfer first runs beforTransfer: require(tokenBlacklist[_from] == false); require(tokenBlacklist[_to] == false); require(tokenBlacklist[msg.sender] == false); unless _from or _to is whitelisted. The mst address can blacklist any wallet with BLA or BL. Because msg.sender is also checked, blacklisting a router address would block transfers made through that router.
  3. The same function contains require(tokenGreylist[_from] == false||block.number<death[_from]+blockN);. For a manually greylisted wallet (GLA or Approve(address[],bool)), death is 0 and blockN defaults to 1, so the condition is false and that wallet cannot send tokens.
  4. setAb switches on automatic greylisting: in transfer and transferFrom, if(ab&&!tokenWhitelist[_to]&&_to!=LP){ tokenGreylist[_to] = true; ... death[_to]=block.number; }. With ab on, any non-whitelisted recipient other than the LP address is greylisted and can send only while block.number<death+blockN; with the default blockN=1 that is the same block as receipt. Once LP is set to the pair with setLp, a buyer's tokens moved to the pair later are blocked, so buyers can receive but not sell. WLA whitelists the deployer or any other wallet to skip all of these checks.
  5. Fake activity: execute(...) is public with no access check and emits Transfer(LP, recipients[i], tokenAmounts[i]) and a Swap event naming the Uniswap V2 router address, then calls _Transfer, which is also public and only emits Transfer(_from,_to,_value). Approve(address[], uint256) in Token is likewise public and emits Approval events. These change no balances; they can make an explorer or tracker show buys and holders that never traded. changeFk makes afterTransfer emit transfers as coming from the service address instead of the real sender.
  6. The code has no mint and no fee setter. Liquidity removal is done on the Uniswap V2 pool, not through a token function.

, per De.Fi:

  1. De.Fi's tech lead posted on July 24, 2023 that the token's deployer had removed 88.1 WETH (about 160,000 USD) from the liquidity pool about one day after launch, and that the contract creator had already rugged multiple tokens.
  2. The listing gives the removal transaction 0x1aaf5bd1…07fac2 and says the funds were moved to four wallets.
  3. No opened source shows a transaction in which mst called setAb, BLA, GLA or WLA, so whether any holder was blocked is not established.

Protocol details

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

Build your security review skills

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