CatgeSwap Rug Pull
What happened
De.Fi's REKT database lists CatgeSwap (CATGES) as an exit scam / rug pull dated 2021-08-30 on Binance, with no loss amount. The verified contract at 0xf9eF4932…090E0A (deployed 2021-06-24 UTC by 0xDc8C749a…1557e4) is a MasterChef farm. In this code, withdraw and emergencyWithdraw reduce a staker's recorded balance but never transfer the staked LP tokens back, and the only function that moves pool LP tokens out sends every pool's balance to feeAddress.
Deposits therefore flow in with no exit for the staker. The code shows this design; no transaction opened for this record shows the sweep was called.
How it happened
- Stakers call
deposit, which pulls LP tokens into the farm withpool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);and creditsuser.amount. The owner can set a pool deposit fee up to 100% (require(_depositFeeBP <= 10000, "add: invalid deposit fee basis points");). withdrawnever returns tokens. After the lock check it only runsif(canWithdraw(_pid, msg.sender)){ if(_amount > 0){ user.amount = user.amount.sub(_amount); }and updatesrewardDebt, then emitsWithdraw. There is nosafeTransferto the caller in the function. The first call also setsuser.withdrawLockedUp = block.timestamp.add(pool.withdrawInterval);, up to 14 days.emergencyWithdrawsetsuser.amount = 0;and emitsEmergencyWithdraw(msg.sender, _pid, amount);with no transfer either, so calling it erases the staker's recorded stake and pays nothing.- Reward harvesting is separate:
depositcallspayOrLockupPendingCatge(_pid);even when_amountis 0, so reward tokens can still be paid while principal cannot be withdrawn. - The one path that moves pool LP tokens out is
migrate():lpToken.safeTransfer(feeAddress, bal);for every pool, wherebalis the farm's full balance of that LP token. It is called fromsetFeeAddress, which requiresmsg.sender == feeAddressand thenrequire(feeAddress == _feeAddress, "setFeeAddress: SAME");, so the caller passes its own address and the whole sweep executes.feeAddressis a constructor argument that the source does not reveal. - On the token side, each taxed transfer debits the sender in full: 6% by default, half to
BURN_ADDRESSand half to the hard-codedFEE_ADDRESS0x571AcF12…E94208, withmaxTransferAmountRate = 50(0.5% of supply) as the default per-transfer cap for non-excluded addresses. The operator can change the cap withupdateMaxTransferAmountRate. - The source does not show that
setFeeAddresswas called or that any holder tried to withdraw.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.