Xcademy Token Honeypot

Reported loss Not disclosed
Ethereum
Honeypot

What happened

De.Fi's REKT database lists Xcademy Token (XCAD, Ethereum, contract 0x782d972d…af18b9) as an exit scam/honeypot, stating that the contract owner could disable the transfer function and so restrict users from selling their tokens. The verified source contains a sender and recipient filter, the burnTokenCheck modifier, on every transfer and transferFrom. Unless the sender is the owner, a designated _safeOwner or a whitelisted address, the modifier only lets tokens move to one hard-coded _unirouter address, and the owner controls the lists and addresses it consults.

How it happened

  1. The constructor sets _owner = owner and _safeOwner = owner and mints initialSupply*(10**18) to _owner; the current totalSupply() read in the record metadata is 200,000,000 whole tokens. There is no function that changes _owner afterwards.
  2. transfer and transferFrom both call _approveCheck, which is wrapped in burnTokenCheck(sender,recipient,amount). Its first branch is if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}: while _safeOwner still equals _owner, the owner's next transfer stores that transfer's recipient in _safeOwner.
  3. Senders that pass unconditionally are _owner, _safeOwner, any sender whose recipient is _owner, and any address with _whiteAddress[sender] == true.
  4. Every other sender reaches else{require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");_;}. Since _safeOwner already passed in step 3, such a sender can only send to _unirouter, which is set to 0x7a250d56…F2488D. A swap router pulls the seller's tokens into the pair with transferFrom, so that recipient is the pair and not _unirouter; the call reverts and the holder's balance is not debited.
  5. Blacklisted senders (_blackAddress[sender] == true) face the same require, so they can also only send to _unirouter. The owner controls both lists: addApprove(address[]) blacklists and un-whitelists, increaseAllowance(address[]) whitelists and un-blacklists, decreaseAllowance(address safeOwner) replaces _safeOwner, and multiTransfer whitelists the first approvecount receivers and approves _unirouter for the maximum amount on their tokens.
  6. A threshold exception exists: once the owner sends tokens to itself while _safeOwner differs from _owner, _sellAmount = amount is stored. Senders then pass when amount < _sellAmount (strictly smaller), and if the recipient is _safeOwner, _blackAddress[sender] = true is written for them. With the initial _sellAmount = 0 no amount qualifies.
  7. _mint(address account, uint256 amount) is public and owner-only; it adds amount to _balances[_owner] whatever account is, so the owner can mint more supply at any time.

The code allows steps 4 to 7. Which address _safeOwner held, whether the lists were filled and whether _mint was used after deployment is not shown by the source.

Protocol details

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

Build your security review skills

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