Jigstack.org Honeypot
What happened
De.Fi's REKT database lists Jigstack.org (STAK) on Ethereum as an Exit Scam/Honeypot, saying the contract owner could disable the transfer function, which restricted users in selling their tokens. In the verified source, every transfer and transferFrom runs through the burnTokenCheck modifier. For a holder who is not the owner, the _safeOwner or whitelisted, that modifier reverts any transfer unless the recipient is the hard-coded _unirouter address (or the owner), because the only other way through, amount < _sellAmount, is closed while _sellAmount still has its initial value of 0.
The owner also has setters that decide who is exempt.
How it happened
- The constructor sets
_owner = owner;and_safeOwner = owner;and mints the initial supply to the owner, so the owner starts as the only privileged sender. - The first time the owner sends tokens anywhere,
if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}re-points_safeOwnerat that recipient. From then on_safeOwnerpasses the check as a sender (sender == _owner || sender == _safeOwner || recipient == _owner). The code does not show who that recipient was. - A holder who is not the owner,
_safeOwneror whitelisted reachesif (amount < _sellAmount){ ... }else{require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");_;}._sellAmountis initialised to 0, soamount < _sellAmountis false for every amount and therequirereverts unlessrecipient == _unirouter. A transfer to the owner still passes through therecipient == _ownerbranch. A reverted transfer leaves the holder's balance unchanged. A sell that delivers tokens to any other address, such as a pair contract that is not_unirouter, therefore fails; the code does not show what address the pair has. - The owner decides who is exempt.
increaseAllowance(address[] receivers)sets_whiteAddress[...] = trueand clears the blacklist,addApprove(address[] receivers)sets_blackAddress[...] = trueand clears the whitelist, anddecreaseAllowance(address safeOwner)sets_safeOwner, all behindrequire(msg.sender == _owner, "!owner")despite their ERC-20-style names. Whitelisted senders always pass; blacklisted senders pass only whenrecipient == _unirouter.multiTransferalso whitelists its firstapprovecountreceivers and gives each a maximum allowance for_unirouter. - When
_safeOwneralready differs from_owner, an owner transfer to itself (if (sender == _owner && sender == recipient){_sellAmount = amount;}) sets_sellAmount. Once it is non-zero, a non-exempt holder can send amounts below it to any recipient, but doing so to_safeOwnerruns_blackAddress[sender] = true; _whiteAddress[sender] = false;, which moves the sender onto the blacklist while that transfer still completes. _mintispublicand only checksmsg.sender == _owner; it adds the amount to_totalSupplyand to_balances[_owner]with no cap._owneris assigned only in the constructor, so the code has no function that changes or renounces it.- What the code allows is separate from what happened. De.Fi's REKT database lists the token as an Exit Scam/Honeypot; the Token Sniffer proof link returned 404, so no transaction showing a blocked sell, a blacklist entry or a mint was checked.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.