XSPACE Honeypot

Reported loss Not disclosed
BNB Chain
Honeypot

What happened

XSPACE (xSpace) is a BEP-20 token on BNB Smart Chain deployed from 0x9CF5F6f2…06E6bE at contract address 0x91eb90b0…000c34. Every transfer and transferFrom runs through a burnTokenCheck modifier that lets only the owner, a stored _safeOwner, whitelisted addresses and transfers to a hard-coded _unirouter address move tokens; any other holder's transfer reverts with a misleading "transfer amount exceeds balance" message. The owner alone can edit the whitelist and blacklist, change _safeOwner and mint more tokens.

De.Fi's REKT database lists XSPACE as an Exit Scam/Honeypot in which the contract owner could disable the transfer function, restricting users from selling; it gives no loss figure and no transaction hashes.

How it happened

  1. The constructor stores the owner and mints the supply to it: _owner = owner; _safeOwner = owner; _mint(_owner, 1000000000000000000000000); with _decimals = 9, i.e. 10^15 tokens. _mint requires msg.sender == _owner, so the deployment succeeding means the deployer 0x9CF5F6f2…06E6bE is the owner. No function assigns _owner again, so ownership cannot be moved or renounced.
  2. transfer and transferFrom both call _approveCheck, which carries the burnTokenCheck(sender,recipient,amount) modifier. Its first branch is if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}: while _safeOwner still equals the owner, the owner's first outgoing transfer makes its recipient the new _safeOwner.
  3. Privileged paths pass: if (sender == _owner || sender == _safeOwner || recipient == _owner) runs the transfer, and if (_whiteAddress[sender] == true){ _; } also runs it. Everyone else falls to the last branches.
  4. A blacklisted sender, and any other sender when amount >= _sellAmount, must satisfy require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"). _sellAmount starts at 0, so amount < _sellAmount is never true until the owner sets it. For these holders the only allowed destination is _unirouter, hard-coded as 0x96e2308c…7Fe5a1 (what that address is on chain was not checked). A sell that moves tokens from the holder to a trading pool is allowed only if the pool is that address; otherwise it reverts, even though the balance is sufficient. Transfers out of the pool to buyers likewise pass only if the pool is _safeOwner or whitelisted.
  5. The owner controls the lists. increaseAllowance(address[]) (despite its name) sets _whiteAddress = true and _blackAddress = false; addApprove(address[]) sets _blackAddress = true and _whiteAddress = false; decreaseAllowance(address safeOwner) overwrites _safeOwner; multiTransfer whitelists the first approvecount receivers and approves _unirouter for the maximum amount. All require msg.sender == _owner.
  6. The owner can also open a side door: a transfer from the owner to itself sets _sellAmount = amount. After that, a non-listed sender sending less than _sellAmount passes, and if the recipient is _safeOwner that sender is added to _blackAddress (if(recipient == _safeOwner){_blackAddress[sender] = true; _whiteAddress[sender] = false;}), so the same holder is restricted afterwards.
  7. _mint(address account, uint256 amount) is public but requires msg.sender == _owner, and it credits _balances[_owner] regardless of account, so the owner can create more supply.
  8. What happened on chain is not established here: no transaction showing a blocked sell, a list edit, a mint or liquidity removal was opened. Only De.Fi's REKT database listing states that selling was restricted.

Protocol details

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

Build your security review skills

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