XSPACE 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
- 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._mintrequiresmsg.sender == _owner, so the deployment succeeding means the deployer 0x9CF5F6f2…06E6bE is the owner. No function assigns_owneragain, so ownership cannot be moved or renounced. transferandtransferFromboth call_approveCheck, which carries theburnTokenCheck(sender,recipient,amount)modifier. Its first branch isif (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}: while_safeOwnerstill equals the owner, the owner's first outgoing transfer makes its recipient the new_safeOwner.- Privileged paths pass:
if (sender == _owner || sender == _safeOwner || recipient == _owner)runs the transfer, andif (_whiteAddress[sender] == true){ _; }also runs it. Everyone else falls to the last branches. - A blacklisted sender, and any other sender when
amount >= _sellAmount, must satisfyrequire((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance")._sellAmountstarts at 0, soamount < _sellAmountis 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_safeOwneror whitelisted. - The owner controls the lists.
increaseAllowance(address[])(despite its name) sets_whiteAddress = trueand_blackAddress = false;addApprove(address[])sets_blackAddress = trueand_whiteAddress = false;decreaseAllowance(address safeOwner)overwrites_safeOwner;multiTransferwhitelists the firstapprovecountreceivers and approves_unirouterfor the maximum amount. All requiremsg.sender == _owner. - 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_sellAmountpasses, and if the recipient is_safeOwnerthat sender is added to_blackAddress(if(recipient == _safeOwner){_blackAddress[sender] = true; _whiteAddress[sender] = false;}), so the same holder is restricted afterwards. _mint(address account, uint256 amount)ispublicbut requiresmsg.sender == _owner, and it credits_balances[_owner]regardless ofaccount, so the owner can create more supply.- 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
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.