GDAO Rug Pull
What happened
GDAO (contract 0xeB0dafA8…fDb904) is a BEP-20 token trading on PancakeSwap; CertiK's alert of 2022-10-28 called it an exit scam with 96% slippage and about 90K USD taken, and warned that roughly 30 other tokens share the GDAO symbol, so the address must be checked. De.Fi's listing, which describes it as a copycat token, says the deployer sent 1,000,000 GDAO to an externally owned account that drained the liquidity and moved the funds through TransitSwap for a 90,302 USD profit. The verified source gives the owner several powers over holders: it can lock any address so it cannot transfer or approve, close the buy gate with a lock time, and set recorded purchase amounts that route a holder's sales through a fee branch, while the token contract contains no liquidity lock; no opened source shows any of these being used.
Addresses listed by De.Fi: 0x7f12728d…6d5d71 and 0xb67b4aa8…878c77. Token transfer transaction listed by De.Fi: 0xb0ed3561…85dbf3.
How it happened
- The owner can freeze a holder:
function setLockAccount(address target, uint256 lockenddate) public onlyOwner {writes an end time, andtransfer,approve,increaseAllowanceanddecreaseAllowancecheck the caller whiletransferFromchecks the token holder:require(block.timestamp>lockAccount[sender]. A router sale callstransferFromwith the holder assender, so a locked holder cannot sell, and cannot approve the router either. - The trade logic keys on
capitalPoolAddr, which isaddress(0)untilfunction setCapitalPoolAddr(address _address) public onlyOwner{sets it. For buys from that address the contract requiresrequire(locktime<=block.timestamp || (getNftWhites(to)==true && buyUamount <= 301*10**18), "Purchase after adding whitelist, please purchase 300U quota");, and the owner can push the date out withfunction setLockTimeOut(uint256 endtime) public onlyOwner {, leaving only owner-whitelisted buyers with a 300 USDT limit. - For sales into
capitalPoolAddr,if(balance.sub(amount) < totalBuyAmount) {sends the sale through a fee branch that splits the amount among the dead address,marketAddrandchangeAddr, and forwards to the pool onlyuint256 toAmount = amount1.mul(sellMulFee).div(10000);. The seller is debited each slice. The rates are constructor arguments not visible in the source, and the owner can force this branch for any address withfunction setBuyAmountListOut(address _address,uint256 _amount) public onlyOwner {. - Fee-exempt status is not protected:
function excludeFromFees(address account, bool excluded) public {has no access modifier, so anyone can mark any address exempt and skip the fee branch. - The token contract has no function that locks or burns liquidity-provider tokens: the
addLiquidityhelper sends LP toaddress(0)but is reachable only throughif(AmountLiquidityFee > 0) swapAndLiquify(AmountLiquidityFee);, andAmountLiquidityFeeis a private variable that is never assigned. Custody of the LP tokens cannot be read from the source. - What happened, per CertiK and De.Fi: the pool was drained, CertiK measured 96% slippage and about 90K USD was taken, with 1,000,000 GDAO sent from the deployer to an externally owned account first. No opened source shows which of the owner functions above, if any, was called.
Protocol details
Evidence
- report CertiK Alert post on GDAO exit scam (via fxtwitter) twitter.com
- transaction bscscan.com transaction 0xb0ed…dbf3 bscscan.com
- address BscScan verified source for GDAO (GdaoToken) bscscan.com
- address bscscan.com address 0x7f12…5d71 bscscan.com
- address bscscan.com address 0xb67b…8c77 bscscan.com
- analysis Sourcify repository entry for GDAO repo.sourcify.dev
- analysis De.Fi REKT database listing for GDAO de.fi
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.