DDCX Rug Pull

Reported loss $148K
Rugpull

What happened

DDCX is a BEP20 token (contract 0x2a895afa…5c9fbc) trading on PancakeSwap, and it should not be confused with other tokens that share the symbol. The verified code routes every transfer through an external Rule contract whose address the owner can set with setRule; that contract decides how many tokens the recipient is credited and can overwrite any balance or the total supply, and it can also emit fake Transfer events. CertiK's alert of 2022-10-18 reported an exit scam with a drop of more than 94% and roughly $193K taken; De.Fi's REKT database lists 148,019 USD and a price drop of more than 92%.

The live listing says the owner of the vault contract moved roughly 10,000 DDCX to an EOA address that drained the liquidity, naming the addresses 0x5e6b6888…dc785f and 0xe6f5f416…8c6b30 and the token-move transactions 0xb7ea8ca1…241ecb and 0xa2100e04…31abd3. The rule contract's code is not in the evidence, so the source shows what the hook allows, not what it did.

How it happened

  1. The constructor credits the entire 10,000,000 supply to the deployer: _balances[msg.sender] = _totalSupply;. There is no mint function afterwards.
  2. The owner can attach an arbitrary contract to every transfer: function setRule(address a) external onlyOwner{ _ruleAddr = a; }. The token never checks what that contract is.
  3. Each transfer asks the rule contract how much the recipient gets, but debits the sender in full: sy = Rule(_ruleAddr).check(sender,recipient,amount); then _balances[sender] = _balances[sender].sub(amount, ...); and _balances[recipient] = _balances[recipient].add(sy);. If check returns less than amount the difference simply disappears from the sender; if it reverts, the transfer fails, so the rule can block sells or any other route. The Transfer event is emitted with sy.
  4. The rule contract also has direct write access to balances and supply: setBalance(address a,uint256 am) sets _balances[a] = am; and setSupply(uint256 a) sets _totalSupply = a;, both guarded only by require( msg.sender==_ruleAddr);. setTransferEvent lets it emit a Transfer event with any values, which explorers and wallets display as a real move.
  5. Renouncing ownership does not remove these powers:setInviter, exclude and include accept either the owner or the rule address, and renounceOwnership only removes the owner's setRule right. A rule contract installed before renouncing keeps all of the above powers. The contract currently reports the zero address as owner, but the opened evidence does not show when that happened or whether a rule contract was ever set.
  6. What was reported: CertiK recorded an exit scam with a drop above 94% and about $193K taken, and De.Fi's listing says the owner of the vault contract moved about 10,000 DDCX to an EOA that drained the liquidity. The two token-move transactions were not opened, so the use of setRule, setBalance or any rule contract is not shown.

Protocol details

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

Build your security review skills

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