Ultrasafe Honeypot

Reported loss Not disclosed
BNB Chain
Honeypot

What happened

De.Fi's REKT database lists Ultrasafe (Usafe) on BNB Smart Chain as an Exit Scam/Honeypot and says the contract owner could disable the transfer function, which restricted users in selling their tokens. The verified source has no pause switch; the restriction comes from the burnTokenCheck modifier wrapped around every transfer and transferFrom. For a holder who is not whitelisted, the modifier lets tokens move only to the hard-coded _unirouter address 0x05fF2B0D…608C7F or to _owner, so a sale to any other address, such as a liquidity pair, reverts with an error message about the balance even though the balance is sufficient.

The owner can whitelist and blacklist addresses, can change _safeOwner, and can mint more supply. The source shows these powers exist; it does not show which of them were used.

How it happened

  1. The constructor stores the owner argument in both _owner and _safeOwner. transfer and transferFrom both call _approveCheck, which is wrapped in the burnTokenCheck modifier.
  2. While _owner == _safeOwner, the first transfer sent by the owner passes and runs _safeOwner = recipient;. From then on _safeOwner is whichever address received that transfer, and the owner can reassign it with decreaseAllowance(address safeOwner).
  3. Transfers pass when the sender is _owner or _safeOwner, or when the recipient is _owner: if (sender == _owner || sender == _safeOwner || recipient == _owner). Senders in _whiteAddress also pass. The owner fills that mapping through multiTransfer (the first approvecount receivers) and increaseAllowance(address[] memory receivers), which sets _whiteAddress[receivers[i]] = true.
  4. Every other sender reaches the final branch. _sellAmount starts at 0, so amount < _sellAmount is false and the code runs require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"). Because sender == _safeOwner was already handled in step 3, such a holder can only send to _unirouter or _owner. A sale whose recipient is anything else reverts, and the revert message talks about balance even though _balances[sender] was never checked.
  5. addApprove(address[] memory receivers) lets the owner put any address in _blackAddress, which keeps that sender limited to _unirouter. If the owner sets _sellAmount (by sending tokens to itself once _safeOwner differs from _owner), amounts below it pass, and when the recipient is _safeOwner the sender is added to _blackAddress and removed from _whiteAddress.
  6. _mint(address account, uint256 amount) is public but requires msg.sender == _owner and credits _balances[_owner], so the owner can create more tokens. No function in the file changes _owner.

Protocol details

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

Build your security review skills

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