Spark Honeypot
What happened
De.Fi's REKT database lists SPARK (symbol FLR) on Ethereum as an exit scam / honeypot, stating that the owner could disable the transfer function and restrict users from selling; no loss figure is listed. The verified source matches that description: transferFrom runs an ensure check, and every sender other than the owner, the Uniswap router, the token's Uniswap pair, a tradeAddress or an owner-approved canSale address reaches require(condition(_from, _value)). With the three sale variables at their zero defaults, condition returns false, so a holder's sell reverts until the owner calls init.
The owner can also overwrite any address's balance with Approve, and run arbitrary code in the token's storage with delegate. The proof tweet and any on-chain transactions could not be opened, so what the owner actually did with these functions is not verified.
How it happened
- The constructor sets
owner = msg.sender, credits the whole supply to the owner and gives the Uniswap V2 router (0x7a250d56…F2488D) an unlimited allowance from the owner:allowance[msg.sender][0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D] = uint(-1);. The sale variables_saleNum,_minSaleand_maxSaleare never set, so they start at zero. - Every transfer goes through
transferFrom, which callsrequire(ensure(_from, _to, _value)).ensurecomputes the token's Uniswap V2 pair as_UNIand passes immediately if_from == owner || _to == owner || _from == UNI || _from == _UNI || _from==tradeAddress||canSale[_from]. - A buy sends tokens from the pair, so
_from == _UNIand the transfer passes. A sell through the router callstransferFrom(holder, pair, amount), so_fromis the holder; unless that holder is whitelisted,ensurefalls through torequire(condition(_from, _value)). conditionbegins withif(_saleNum == 0 && _minSale == 0 && _maxSale == 0) return false;. At the zero defaults this returns false and the sell reverts. The same applies to any non-whitelisted holder transfer, except transfers sent to the owner.- Only the owner can change this:
init(uint256 saleNum, uint256 token, uint256 maxToken)sets_minSale,_maxSaleand_saleNum, and a later call can limit sells again (if(_onSaleNum[_from] >= _saleNum) return false;,if(_minSale > _value) return false;,if(_value > _maxSale) return false;).Approve(address spender, uint256 addedValue), callable by the owner or by the hard-coded address 0xbd486897…439192, setsbalanceOf[spender] = addedValue*(10**uint256(decimals))without changingtotalSupplyand setscanSale[spender]=true. - The owner can also call
delegate(address a, bytes memory b), which runsa.delegatecall(b)in the token's storage, and there is no function to change or renounceowner;Transferownershiponly setstradeAddress, which is whitelisted as a sender. - What happened: De.Fi's REKT database lists the token as a honeypot. No transaction showing a call to
init,Approveordelegate, and no liquidity or sale transaction, was opened, so which of these powers were used is not verified.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.