MoonElevator Honeypot
What happened
MoonElevator (ELEVATOR) is a token on BNB Smart Chain whose transfer logic runs through a burnTokenCheck modifier. Senders who are not the owner, the owner-set _safeOwner, or on the owner's whitelist may only send tokens to the hard-coded _unirouter address or to the owner (amounts below the owner-set _sellAmount are the exception); any other recipient reverts with 'ERC20: transfer amount exceeds balance', although the sender's balance is untouched. The owner can whitelist or blacklist addresses, change _safeOwner, and set a _sellAmount threshold through functions with misleading names.
De.Fi's REKT database lists the token as an Exit Scam/Honeypot and says the owner could restrict users from selling; it gives no loss figure.
How it happened
- The constructor sets
_owner = owner; _safeOwner = owner;and mints the initial supply to the owner._mintispublicbut requiresmsg.sender == _owner, so the owner can mint more later. - Every
transferandtransferFromcalls_approveCheck, which is wrapped inburnTokenCheck(sender,recipient,amount). First branch:if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}. While_safeOwnerstill equals the owner, the owner's first outgoing transfer stores its recipient as_safeOwner. - Privileged paths pass:
if (sender == _owner || sender == _safeOwner || recipient == _owner)lets the transfer through. Inside it,if (sender == _owner && sender == recipient){_sellAmount = amount;}lets the owner set_sellAmountby sending tokens to itself. Anyone can therefore send tokens to the owner address. - For other senders:
if (_whiteAddress[sender] == true){ _; }passes with no restriction. A blacklisted sender hitsrequire((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"), and since_safeOwnerwas already handled above, it can only send to_unirouter. - A sender on neither list:
if (amount < _sellAmount)the transfer passes, and ifrecipient == _safeOwnerthe sender is added to_blackAddressand removed from_whiteAddress(the transfer still completes). Otherwise the samerequireapplies._sellAmountstarts at 0 (uint256 private _sellAmount = 0;), so until the owner raises it no amount is below it, and an ordinary holder can only send to_unirouteror the owner. Any other recipient, such as a pair contract, reverts. - Control sits with the owner:
increaseAllowance(address[])whitelists,addApprove(address[])blacklists,decreaseAllowance(address safeOwner)replaces_safeOwner, andmultiTransfercan whitelist the firstapprovecountreceivers and give them an unlimited allowance to_unirouter. The code has no function that changes_owneror renounces it. - What happened on-chain is not established here: no opened source shows who was whitelisted or blacklisted, what
_safeOwneror_sellAmountheld, or any losses.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.