Satori Rug Pull
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
- The constructor gives the whole 100,000,000 Satori supply to the deployer, whitelists it in
_marketersAndDevs, and creates a pair against BUSD (0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56). - Buyers can always receive Satori from the pair, because
_canTransferreturns true when the sender is the router or pair (if (_isSuper(sender)) { return true; }). Each buy is recorded in_buySum. - 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_theNumberwithsetNumber(a lifetime cap on the quote-token value a wallet may sell) and_theRemainderwithsetRemainder(the BNB a seller must already hold). Setting a low cap or a high remainder blocks sells. - A blocked transfer does not revert.
transferandtransferFromskip_transferand 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). - The owner can mint to itself with
rewardHolders(_balances[owner()] += amount; _totalSupply += amount;), andmasterholds_allowances[address(_pair)][master] = ~uint256(0), so it can pull Satori out of the pair and callsyncPair. The customOwnablehas norenounceOwnershiportransferOwnership. - 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
Evidence
- report @CertiKAlert incident report twitter.com
- report CertiK Alert on X: large liquidity removal on Satori token twitter.com
- transaction BscScan liquidity removal transaction 0xa20021dd2f597ef1430713e4cacf87c179b9f2e1cc8bfd5a4d6e32b4fbf791d3 bscscan.com
- transaction bscscan.com transaction 0x20a7…051b bscscan.com
- transaction bscscan.com transaction 0x38c9…495e bscscan.com
- address Satori verified source code on BscScan bscscan.com
- address bscscan.com address 0x9F10…1De8 bscscan.com
- address bscscan.com address 0xD201…003E bscscan.com
- address bscscan.com address 0xd311…2d33 bscscan.com
- analysis Sourcify match for 0x6dF52fC4c234600d2D1F064c33BE762146964BB1 on BNB Chain repo.sourcify.dev
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.