DUO Rug Pull

Reported loss $334K
Rugpull

What happened

DUO is a BEP20 token on BNB Smart Chain (0x1ED990bd…F1343A) traded on PancakeSwap. Its transfer logic only lets an ordinary holder sell what that wallet bought through the pair, blocks ordinary wallet-to-wallet transfers, and gives the owner two settings that can block every sell. A privileged master address also holds a maximum allowance over the pair's token balance.

CertiK Skynet alerted that the deployer removed 352.9K USD of WBNB liquidity in 3 transactions over a 4 day period. De.Fi's REKT database lists the incident as an exit scam/rugpull of 334,330 USD removed from PancakeSwap on Sep 23, 2023, with the loss totalling 1574.89 BNB; per the same listing, some of the funds were distributed to other wallets and withdrawn through Tornado Cash, and the rest were in the creator's wallet as of Sep 30, 2023.

Deployer address (per De.Fi listing): 0x6F238E68…1D261E

Scammer addresses (per De.Fi listing): 0x5C4Eb44a…Fe113B, 0xaf991f71…3b4673, 0x79234044…A40B02

Liquidity removal transactions (per De.Fi listing): 0x58e6d53a…92d2a4, 0x932aeb6f…3dde84, 0xdaffe751…bcc8a3

How it happened

What the verified code allows:

  1. The constructor mints the full supply to the deployer, makes the deployer master, whitelists it, and gives master an unlimited allowance over the pair's tokens: _allowances[address(_pair)][master] = ~uint256(0); and _marketersAndDevs[owner()] = true;.
  2. Transfers between ordinary wallets do nothing. _canTransfer ends with return false; for them, and transfer still returns true without moving tokens: if (_canTransfer(_msgSender(), recipient, amount)) { _transfer(...); } return true;.
  3. Selling is conditional. For a non-whitelisted seller, a transfer to the router or pair must satisfy return bought >= sold + amount && _theNumber >= soldETH + amountETH && sender.balance >= _theRemainder;. bought only grows when tokens arrive from the router or pair (if (_isSuper(from)) { _buySum[to] += amount; }), so tokens obtained any other way cannot be sold.
  4. The owner can set the other two limits. setNumber lowers the cumulative ETH-equivalent a holder may sell, and setRemainder sets a minimum BNB balance for the seller: function setRemainder(uint256 newRemainder) external onlyOwner { _theRemainder = newRemainder; }. A large value blocks every non-whitelisted sell. There is no renounceOwnership in the contract, so these owner powers cannot be given up.
  5. master can pull tokens out of the pair with transferFrom(pair, recipient, amount) (the sender is the pair, which _isSuper always passes), then call syncPair() so the pair's reserves update to the reduced token balance. The code allows this to move the pool price; no transaction in the evidence shows it was done.
  6. Whitelisted addresses (_marketersAndDevs) skip every check, so the deployer can sell or move tokens while others are restricted. The whitelist can be extended by master through includeInReward.

, per the sources:

7. CertiK Skynet reported that the deployer removed 352.9K USD of WBNB liquidity in 3 transactions over a 4 day period. De.Fi's REKT database lists the incident as an exit scam/rugpull of 334,330 USD (1574.89 BNB), with some funds moved to other wallets and Tornado Cash. The token contract has no liquidity-lock logic, so LP tokens held by the deployer could be redeemed through the normal PancakeSwap router.

Protocol details

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

Build your security review skills

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