UniFarm Honeypot

Reported loss Not disclosed
Ethereum
Honeypot

What happened

UniFarm (UFARM) at 0x91468f2b…b5e5d1 on Ethereum is an ERC20 whose transfer and transferFrom both pass through a burnTokenCheck modifier. _owner and _safeOwner can send freely, and so can whitelisted addresses. With the default _sellAmount of 0, any other holder's transfer goes through only if the recipient is _owner or the hard-coded address _unirouter (0x7a250d56…F2488D); a transfer to any other recipient reverts with the misleading message ERC20: transfer amount exceeds balance.

The deployer, who is _owner, can whitelist or blacklist addresses at will. De.Fi's REKT database lists the token as an Exit Scam/Honeypot and describes it as the contract owner being able to disable the transfer function, which restricted users in selling their tokens. The listing states no loss figure.

How it happened

What the code allows:

  1. The constructor sets _owner = msg.sender; and _safeOwner = msg.sender;, then mints 1,000,000,000 tokens to the owner. No function changes _owner afterwards, and there is no renounceOwnership.
  2. While _owner == _safeOwner, the first transfer sent by the owner takes the first branch of the modifier: if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}. Whichever address receives that first owner transfer becomes _safeOwner.
  3. Otherwise the modifier first checks if (sender == _owner || sender == _safeOwner || recipient == _owner). Those transfers always pass: the owner and _safeOwner can send anywhere, and any sender can send to _owner. In that branch, if (sender == _owner && sender == recipient){_sellAmount = amount;} lets the owner set the threshold used in step 6.
  4. Any other sender (not _owner, not _safeOwner, sending to someone other than _owner) is checked next. A whitelisted sender passes via if (_whiteAddress[sender] == true){ _;}. A blacklisted sender passes only if require((sender == _safeOwner)||(recipient == _unirouter), ...) holds, which for this sender means the recipient is _unirouter.
  5. A sender who is on neither list, with _sellAmount still 0, reaches }else{require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");_;}. The holder's balance is intact, but the transfer reverts unless it goes to _unirouter (or, by step 3, to _owner). A swap that pulls tokens into a pair contract sends them to the pair, not to the router address, so the require fails for that holder.
  6. If _sellAmount has been set above 0, a non-listed sender sending less than _sellAmount is allowed through to any recipient, but if the recipient is _safeOwner the sender is added to _blackAddress by _blackAddress[sender] = true; _whiteAddress[sender] = false;. A non-listed sender sending _sellAmount or more gets the same require as in step 5. A blacklisted sender can afterwards send only to _unirouter or _owner.
  7. The owner controls the exemptions: increaseAllowance(address[]) sets _whiteAddress true (and _blackAddress false); addApprove(address[]) does the reverse; decreaseAllowance(address safeOwner) sets _safeOwner to any address; and multiTransfer whitelists the first approvecount receivers and sets a maximum allowance in their name for _unirouter.
  8. _mint is public but restricted by require(msg.sender == _owner, ...) and always credits _balances[_owner], so the owner can increase supply to itself.

De.Fi's REKT database lists the token as an Exit Scam/Honeypot. No transaction showing the owner calling any of these functions was opened.

Protocol details

Classification Token
Protocol Type Yield
Affected asset / contract UFARM
Protocol links @unifarm_

Security review history

Build your security review skills

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