Peace doge X Rug Pull

Reported loss $18K
Rugpull

What happened

Peace doge X is a BNB Smart Chain token whose verified contract keeps several owner-only switches over trading, fees and blacklisting, and gives the deployer the entire supply at creation. PeckShield Alert posted on 2022-03-08 that the token was a scam, citing a 23% selling fee, deployer funds that originated from Tornado Cash, and an owner able to modify the contract, for example to disable selling or change fees. De.Fi's REKT database lists the project as an exit scam with a loss of 18,144 USD: the project ran a presale on PinkSale (Source), the deployer withdrew liquidity in bscscan.com transaction 0x0ccef5b6…3fc140, and the funds were sent to an external address and deposited into the Tornado Cash mixer in 0xef589ed5…f45d8d.

These two transactions and the presale page are as listed by De.Fi.

How it happened

  1. The constructor passes msg.sender to Auth and sets _rBalance[msg.sender] = rSupply;, so the deployer is the owner and starts with the entire supply. Default fees are 2 + 3 + 3 + 2 = 10 out of feeDenominator = 100, and tradingOpen = true.
  2. On a sell (recipient == pair), takeFee computes rAmount.div(feeDenominator * 100).mul(totalFee).mul(multiplier) with multiplier = sellMultiplier. set_sell_multiplier(uint256 Multiplier) external onlyOwner has no limit, and the 33% check in setFees (require(totalFee < feeDenominator / 3, ...)) does not cover it. With the default fees a multiplier of 1000 makes feeAmount equal to the whole sale, which is credited to the token contract, so the seller receives nothing. Any higher multiplier makes rAmount.sub(feeAmount) revert, so the whole sell transaction fails and the seller's balance is left unchanged.
  3. blacklistMode = true by default, and _transferFrom requires !isBlacklisted[sender] && !isBlacklisted[recipient]. manage_blacklist(address[] calldata addresses, bool status) is onlyOwner, so a listed holder cannot sell (the pair is the recipient) or receive tokens, and listing the pair address would stop trading for everyone. setBot / _isBot blocks the same way and is always enforced.
  4. tradingStatus(bool _status, uint256 _deadBlocks) is onlyOwner. When tradingOpen is false, if (!authorizations[sender] && !authorizations[recipient]) { require(tradingOpen, "Trading not open yet"); } makes every transfer between ordinary holders and the pool revert, while authorized addresses (the owner and anyone the owner authorizes) can still move tokens.
  5. For taking value out, rescueToken(address tokenAddress, uint256 tokens) onlyOwner transfers any BEP20 held by the contract to the owner and rescueBNB sends the contract's whole BNB balance to a chosen address. swapBack and rebase1000 mint liquidity tokens to autoLiquidityReceiver (set to the deployer in the constructor) and to address(this) respectively. The code does not show who added the first liquidity or holds those liquidity tokens.
  6. What happened, per PeckShield's alert and De.Fi's listing: the sell fee was 23% (the constructor default is 10%, so a fee or multiplier setting different from the default was in force; the transaction that set it was not opened), and the deployer withdrew the pool liquidity, moved the proceeds to an external address and deposited them into Tornado Cash, for a listed loss of 18,144 USD.

Protocol details

Classification Token
Protocol Type Exit Scam/Rugpull
Affected asset / contract Peace doge X
Protocol links @PeacedogeX

Build your security review skills

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