BabyDAO Rug Pull
What happened
BabyDAO (BabyDAO) is a fixed-supply BNB Smart Chain token whose _internalTransfer lets a small set of "synced" addresses decide who can trade. The deployer is synced at construction, and any synced address can call claim to mark a holder as claimed; a claimed holder's transfers into the PancakeSwap pair revert with "Claimed", so that wallet can be stopped from selling while buys still work. The owner can also switch trading off for all non-synced addresses with openTrading(false), and the contract has no mint or liquidity lock.
The deployer created the token in tx 0x3af2f636…650e88 (10,000,000,000 tokens to the deployer 0xe153c4b6…29c78a). De.Fi's REKT database lists the token as a rug pull of about $7.6k: a PancakeSwap pair was created in tx 0x8ad196e5…8f183b with 10 WBNB and 100M BabyDAO; about 14 days later 300 BNB reached the deployer (tx 0x166c1ced…231429) and 290 BNB went into the pool (tx 0x67e83954…ccec3c); after the price rose the deployer removed liquidity for a listed profit of 32.25 WBNB, and the funds were sent on to 0xd759da59…eb7f0a (tx 0x40a34d6f…a31ea3) and laundered through Tornado.cash, per the listing.
How it happened
- What the code allows: the constructor gives the whole supply to the deployer and marks the deployer as synced:
_tokenBalances[_msgSender()] = _tokenTotalSupply;and_synced[owner()] = true;._tokenTotalSupply = 1000000000000 * 1e7is 1e19 base units, or 10,000,000,000 tokens at 9 decimals. - Trading starts closed.
_internalTransferonly runs its checks when both parties are unsynced:if (!_synced[sender] && !_synced[recipient]) { require(_isTradingOpen);._isTradingOpendefaults to false and is set only byopenTrading(bool value) public onlyOwner, which can close trading again at any time. Synced addresses skip every check. - Selling can be blocked per wallet.
_isSwapEnabledis set to true in the constructor and has no setter, so unsynced transfers must touch the pair (require(sender == uniswapV2Pair || recipient == uniswapV2Pair);), and a transfer into the pair runsrequire(!_claimed[sender], "Claimed");andrequire(!_claimed[_msgSender()], "Claimed");. A router sale callstransferFrom(holder, pair, amount), sosenderis the holder and a claimed holder's sale reverts. A buy hassender == uniswapV2Pair, never reaches the_claimedcheck and goes through. - The flag is set by synced addresses:
function claim(address[] calldata wallet, bool value) external onlySyncedwrites_claimed[wallet[i]] = value;, andsynclets any synced address add further synced addresses. These functions are gated by_synced, not by ownership, sorenounceOwnershipwould not remove the deployer's synced status. - What happened, per De.Fi's REKT database: the deployer paired the token with BNB on PancakeSwap, added more liquidity, then removed liquidity at a higher price for a listed 32.25 WBNB profit and moved the proceeds to a second address for Tornado.cash. No opened transaction shows whether
claim,syncoropenTrading(false)was called on any holder.
Protocol details
Evidence
- report BabyDAO source (Sourcify) repo.sourcify.dev
- transaction bscscan.com transaction 0x166c…1429 bscscan.com
- transaction bscscan.com transaction 0x3af2…0e88 bscscan.com
- transaction bscscan.com transaction 0x40a3…1ea3 bscscan.com
- transaction bscscan.com transaction 0x67e8…ec3c bscscan.com
- transaction bscscan.com transaction 0x8ad1…183b bscscan.com
- address BabyDAO verified source (BscScan) bscscan.com
- address bscscan.com address 0xd759…7f0a bscscan.com
- address bscscan.com address 0xe153…c78a bscscan.com
- address bscscan.com address 0xf2d5…0f5f bscscan.com
- analysis De.Fi REKT database listing for BabyDAO (proof: DexScreener pair page, returned 403) dexscreener.com
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.