SafeSushi Honeypot

Reported loss Not disclosed
BNB Chain
Honeypot

What happened

SafeSushi (Sushi) is a token on BNB Smart Chain at 0x540dfab4…940ec4, deployed in transaction 0x7ae3e498…8a9f19. De.Fi's REKT database lists it as an Exit Scam/Honeypot and says the contract owner could disable the transfer function, which restricted users from selling. The verified source gates every transfer through a burnTokenCheck modifier with owner-controlled whitelist and blacklist maps: while _sellAmount is 0, an ordinary holder can only send tokens to the hard-coded _unirouter address or to _owner, and the owner can exempt chosen addresses.

The sources opened do not include a transaction showing holders being blocked.

How it happened

  1. Every transfer and transferFrom runs _approveCheck, which is wrapped in the modifier burnTokenCheck(sender,recipient,amount). Only the owner can set its inputs: _whiteAddress, _blackAddress, _safeOwner and _sellAmount, which starts at uint256 private _sellAmount = 0;.
  2. The modifier's first branch is if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}. While _safeOwner still equals _owner, the first transfer out of the owner stores that transfer's recipient as _safeOwner. The owner can also set it directly with decreaseAllowance(address safeOwner). The source does not say which address that was for this token.
  3. Next branch: if (sender == _owner || sender == _safeOwner || recipient == _owner){. Transfers from the owner or _safeOwner, and any transfer sent to _owner, pass.
  4. A whitelisted sender passes (if (_whiteAddress[sender] == true){). The owner can whitelist addresses with increaseAllowance(address[] receivers), and multiTransfer whitelists the first approvecount receivers and gives each a maximum allowance to _unirouter.
  5. Every other sender reaches if (amount < _sellAmount){. With _sellAmount at 0 this is false, so the code runs require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"). An ordinary holder's transfer therefore reverts unless the recipient is _unirouter (or _owner, from step 3). A holder's balance is only debited after the modifier passes, so a revert leaves the tokens in place.
  6. The owner can open a narrow window: if (sender == _owner && sender == recipient){_sellAmount = amount;} sets _sellAmount when the owner sends to themselves, once _safeOwner no longer equals _owner. After that, transfers below _sellAmount pass for non-blacklisted senders, and if the recipient is _safeOwner the code runs _blackAddress[sender] = true; _whiteAddress[sender] = false; so that sender is flagged. A blacklisted sender faces the same recipient-only rule as step 5 with no _sellAmount window. The owner can also blacklist addresses directly with addApprove.
  7. _mint is public but requires msg.sender == _owner and credits _balances[_owner] whatever account is passed, so the owner can add to the supply held by _owner.

Protocol details

Classification Token
Protocol Type Exit Scam/Honeypot
Affected asset / contract Sushi

Build your security review skills

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