Harvester Network Honeypot

Reported loss $6K
Ethereum
Honeypot

What happened

HARVESTER restricts every transfer with a hard-coded rule, not a switch: the ensure modifier only lets a transfer through if the sender is the owner, the recipient is the owner, or the sender is the token's Uniswap V2 pair. A holder selling into the pair is a transfer from the holder to the pair, which fails that rule, so only the owner address can sell, while buys (pair to buyer) and the owner's liquidity removal pass. Etherscan shows the deployer address 0x484cC1De…67f82c added 9 ETH and 50,000 HARVESTER of liquidity on 2020-12-17 (tx 0x9b7ab0b3…aab29b) and on 2020-12-23 removed liquidity with removeLiquidityETHWithPermit, receiving 22,073.2 HARVESTER and 20.44 ETH (tx 0x8648805c…85a039).

The source contains no function that turns the restriction off or on; it applies from deployment.

How it happened

  1. The constructor makes the deployer the owner and gives it the whole supply, plus an unlimited router allowance: owner = msg.sender; / balanceOf[msg.sender] = totalSupply; / allowance[msg.sender][0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D] = uint(-1);.
  2. Every transfer goes through transfer, which calls transferFrom(msg.sender, _to, _value), and transferFrom carries the modifier ensure(_from, _to).
  3. ensure computes the Uniswap V2 pair address for this token and WETH and requires: require(_from == owner || _to == owner || _from == UNI); where the local UNI is that pair. Tokens can therefore leave the pair (buys, liquidity removal), be sent by the owner, or be sent to the owner.
  4. A holder's sale, whether a direct transfer to the pair or a router transferFrom(holder, pair, amount), has the holder as _from and the pair as _to, so none of the three conditions hold and the call reverts. Holder-to-holder transfers fail the same way.
  5. The deployer added 9 ETH and 50,000 HARVESTER on 2020-12-17. On 2020-12-23 it removed the liquidity and received 20.44 ETH and 22,073.2 HARVESTER; that is 11.44 ETH more than it had added, consistent with ETH paid into the pool by other buyers (arithmetic from the two Etherscan pages).
  6. The owner also has function delegate(address a, bytes memory b) public payable { require(msg.sender == owner); a.delegatecall(b); }, which executes arbitrary code against the token's own storage, so the owner could alter balances or the owner variable; no use of it is shown in the opened sources.

Protocol details

Classification Token
Protocol Type Exit Scam/Honeypot
Affected asset / contract HARVESTER
Protocol links Website

Build your security review skills

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