Genesis Shards Honeypot

Reported loss Not disclosed
Ethereum
Honeypot

What happened

Genesis Shards (GS) is an Ethereum token whose transfer and transferFrom both run through an owner-controlled burnTokenCheck modifier. An address that is on neither of the owner's lists can only send tokens to the hard-coded _unirouter address (0x7a250d56…F2488D) or to the owner, unless the amount is below an owner-set _sellAmount; every other transfer reverts with ERC20: transfer amount exceeds balance. De.Fi's REKT database lists Genesis Shards as an Exit Scam/Honeypot dated 2021-04-10 and says the owner could disable the transfer function, which restricted users in selling their tokens.

The code has no single on/off switch for transfers; the restriction comes from the whitelist, blacklist and _sellAmount checks.

How it happened

  1. The first transfer the owner sends while _safeOwner still equals _owner passes and stores its recipient: if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}. The code does not show who that recipient was.
  2. Transfers whose sender is _owner or _safeOwner, or whose recipient is _owner, always pass. An owner transfer to itself also writes the threshold: if (sender == _owner && sender == recipient){_sellAmount = amount;}.
  3. Any other sender found in _whiteAddress passes unchecked. The owner fills that list through increaseAllowance(address[]), and through the first approvecount receivers of multiTransfer, which also grants _unirouter a maximum allowance over their tokens.
  4. A sender in _blackAddress, which the owner fills through addApprove(address[]), must satisfy require((sender == _safeOwner)||(recipient == _unirouter), ...). The _safeOwner half is already handled by step 2, so in practice the recipient must be _unirouter.
  5. Every other holder falls to the last branch. If amount < _sellAmount the transfer runs, and when recipient == _safeOwner the sender is moved onto the blacklist in that same call. Otherwise the same require applies and the transfer reverts unless the recipient is _unirouter (or the owner, via step 2). While _sellAmount is at its initial value of 0 the threshold branch can never run. A router sale moves tokens with transferFrom to the pair address, so it passes only if the pair happens to be one of the addresses these branches accept; the code never names the pair.
  6. The owner can re-point _safeOwner with decreaseAllowance(address safeOwner). _mint is public with only a msg.sender == _owner check, credits _balances[_owner] and has no supply cap. _owner is set once in the constructor and there is no function to transfer or renounce it.

Protocol details

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

Build your security review skills

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