MoltenSwap Rug Pull
What happened
MoltenSwap's MasterChef farming contract, which is also the owner of the MOLT token, has a set() function that looks like a routine pool-settings update but also sends the pool's entire LP-token balance to a feeAddress. LP tokens that users stake in a MasterChef pool are held by the contract, so an owner call to set() can move everything staked in that pool to one wallet. Because it first calls safeApprove(feeAddress, bal), a repeat call on the same LP token reverts while the earlier allowance is unspent, unless an intervening call with a zero balance resets it.
De.Fi's REKT database lists MoltenSwap as an exit scam dated 2021-03-07 with $121,994 lost, and says the contract deployer called set() several times, that calling set() moved LP tokens to an external wallet (example transaction 0x3b8ed32a…53de91), and that this wallet then removed the liquidity in several transactions: 0x31c99d77…e84d55, 0xde655409…4887cc, 0x6c9d7e8b…e4de6b, 0x21d2c374…e69a10, 0x3a0c56b9…8cb1bb, 0xdcd892c2…81b640, 0x9033ed6a…9f1865, 0x0deb3c97…d73e5e and 0x53a1f15d…7da8e0. The listing's list of set() calls is a Bitquery query for method d9638422 on the MasterChef contract. These transactions were not opened here.
How it happened
Steps 1 to 4 describe what the verified code allows; step 5 is what the De.Fi listing reports.
- Users stake LP tokens in the MasterChef:
depositpulls them in withpool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);and records only a number inuser.amount. The staked LP tokens sit in the MasterChef's own balance. - The owner-only
set()is documented as updating a pool's allocation points and deposit fee, but its last lines read:uint256 bal = lpToken.balanceOf(address(this));...lpToken.safeApprove(address(feeAddress), bal);andpool.lpToken.safeTransfer(address(feeAddress), bal);. A call that gets through sends the pool's whole LP balance tofeeAddress, whatever values are passed for_allocPointand_depositFeeBP. - There is a limit on repeating this.
safeApprovein the SafeBEP20 library containsrequire((value == 0) || (token.allowance(address(this), spender) == 0), "SafeBEP20: approve from non-zero to non-zero allowance");, and the MasterChef'ssafeTransfertofeeAddressdoes not spend the allowance it just set. So after one call with a positive balance, anotherset()on the same LP token reverts while that allowance is unspent, unless a call made when the pool's balance is zero resets it (orfeeAddressspends it). Later deposits into that pool can therefore be swept only after such a reset. Pools with different LP tokens are unaffected by each other's allowance. - Users' recorded balances are not reduced.
withdrawandemergencyWithdrawstill pay out of the contract's own LP balance (pool.lpToken.safeTransfer(address(msg.sender), amount);), which stays empty after a sweep unless tokens are returned. The deposit-fee check also permits up to 100%:require(_depositFeeBP <= 10000, ...).setFeeAddresscan only be called by the currentfeeAddress, so the destination is controlled by whoever holds that address. - Reported: De.Fi says the deployer invoked
set()multiple times, that callingset()led to an LP-token transfer to an external wallet, and that the wallet removed the liquidity in nine transactions listed above. The loss figure is $121,994.
Protocol details
Evidence
- transaction bscscan.com transaction 0x0deb…3e5e bscscan.com
- transaction bscscan.com transaction 0x21d2…9a10 bscscan.com
- transaction bscscan.com transaction 0x31c9…4d55 bscscan.com
- transaction bscscan.com transaction 0x3a0c…b1bb bscscan.com
- transaction bscscan.com transaction 0x3b8e…de91 bscscan.com
- transaction bscscan.com transaction 0x53a1…a8e0 bscscan.com
- transaction bscscan.com transaction 0x6c9d…de6b bscscan.com
- transaction bscscan.com transaction 0x9033…1865 bscscan.com
- transaction bscscan.com transaction 0xdcd8…b640 bscscan.com
- transaction bscscan.com transaction 0xde65…87cc bscscan.com
- address MoltToken.sol verified source, BscScan bscscan.com
- analysis MoltToken.sol, Sourcify match repo.sourcify.dev
- analysis MasterChef.sol (MoltenSwap MasterChef 0xb9c5...7070), Sourcify match repo.sourcify.dev
- analysis De.Fi REKT database listing for MOLT (no proof link) de.fi
- analysis explorer.bitquery.io page calls explorer.bitquery.io
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.