Mars Farm Rug Pull

Reported loss $38K
BNB Chain
Rugpull

What happened

The verified MARS Farm source is a minimal token whose transfer function reverts for every holder except the owner, one owner-chosen trading address and wallets the owner whitelists, because the sale-limit variables that would allow ordinary transfers are never assigned anywhere in the code. Buyers can receive tokens only if the pool is exempt, but cannot move or sell them. The constructor puts the whole supply on the deployer 0xDa53cC63…A05BA9, which is the transaction De.Fi's REKT database lists as the deployer minting tokens to its wallet (0x152671f8…9b1c7b).

De.Fi's REKT database lists the incident as a rug pull of 37,645 USD on BNB Chain, with the deployer adding initial liquidity in 0x52037a40…d4b475 and removing it in 0xb74338b1…4ad986.

How it happened

  1. The constructor gives the entire supply to the deployer: balanceOf[msg.sender] = totalSupply; with owner = msg.sender;. The source comments claim ZERO developer premine and an INACCESSIBLE mint function, but the code assigns all tokens to the deployer.
  2. Every transfer calls ensure: if(_from == owner || _to == owner || _from == tradeAddress||canSale[_from]){ return true; } require(condition(_from, _value));. Only the owner, the single tradeAddress and wallets in canSale get a free pass.
  3. For everyone else condition decides, and it is a dead end: if(_saleNum == 0 && _minSale == 0 && _maxSale == 0) return false;. Those three variables are declared private with default value 0, and no function in the source writes to _saleNum, _minSale or _maxSale, so the early return false always runs and require(condition(_from, _value)) reverts. A holder's sell is a transfer from the holder to the pool, so it reverts.
  4. Buys still work only when the pool is exempt: a purchase is a transfer with the pool as _from, which needs _from == tradeAddress or canSale[_from]. The owner sets those with function transferownership(address addr) (sets tradeAddress = addr;) and function approveAndCall(address spender, uint256 addedValue) (sets canSale[spender]=true;). Both are restricted by require(msg.sender == owner);, and the source has no function to change the owner or give these powers up.
  5. approveAndCall also overwrites a balance without touching supply: if(addedValue > 0) {balanceOf[spender] = addedValue*(10**uint256(decimals));}, so the owner can set any address's balance to an arbitrary amount while totalSupply stays unchanged.
  6. What happened, per De.Fi's REKT database: the deployer added initial liquidity and later removed it, with 37,645 USD listed as lost. The liquidity transactions could not be opened here, so amounts, timing and who held the LP tokens are not independently confirmed, and no transaction was opened showing the owner calling transferownership or approveAndCall.

Protocol details

Classification Borrowing and Lending
Protocol Type Exit Scam/Rugpull
Affected asset / contract MARS
Protocol links Website @farm_mars

Build your security review skills

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