Satori Rug Pull

Reported loss $226K
Rugpull

What happened

Satori was a BEP20 token on BNB Chain whose contract, created with a Satori/BUSD PancakeSwap pair, lets only whitelisted addresses and the PancakeSwap router or pair move tokens freely. For everyone else, a transfer to another wallet silently does nothing, and a sell to the pair only works if it passes three checks, two of them owner-tunable: the wallet cannot sell more Satori than it bought from the pair, the BUSD value it has ever sold must stay under an owner-set cap, and the seller must hold an owner-set minimum BNB balance. The owner can also mint unlimited Satori to itself, and a master address holds an unlimited allowance over the pair's Satori balance.

On 6 August 2023 CertiK flagged the token as an apparent honeypot and reported that the deployer removed liquidity in 8 transactions over three days. De.Fi's listing counts nine removals between 2 and 7 August 2023 worth about $225,762, moved through several addresses to a holding address.

How it happened

  1. The constructor gives the whole 100,000,000 Satori supply to the deployer, whitelists it in _marketersAndDevs, and creates a pair against BUSD (0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56).
  2. Buyers can always receive Satori from the pair, because _canTransfer returns true when the sender is the router or pair (if (_isSuper(sender)) { return true; }). Each buy is recorded in _buySum.
  3. A holder's sell is a transfer to the pair, which passes only if bought >= sold + amount && _theNumber >= soldETH + amountETH && sender.balance >= _theRemainder. The owner can change _theNumber with setNumber (a lifetime cap on the quote-token value a wallet may sell) and _theRemainder with setRemainder (the BNB a seller must already hold). Setting a low cap or a high remainder blocks sells.
  4. A blocked transfer does not revert. transfer and transferFrom skip _transfer and still return true, so the router's swap then fails because the pair never receives the tokens. Wallet-to-wallet transfers are blocked the same way (return false; at the end of _canTransfer).
  5. The owner can mint to itself with rewardHolders (_balances[owner()] += amount; _totalSupply += amount;), and master holds _allowances[address(_pair)][master] = ~uint256(0), so it can pull Satori out of the pair and call syncPair. The custom Ownable has no renounceOwnership or transferOwnership.
  6. CertiK reported that the deployer removed liquidity in 8 transactions over three days; De.Fi's listing counts nine removals from 2 to 7 August 2023 worth about $225,762.

Protocol details

Classification Token
Protocol Type Exit Scam/Rugpull
Affected asset / contract Satori

Build your security review skills

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