UniFarm 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:
- The constructor sets
_owner = msg.sender;and_safeOwner = msg.sender;, then mints 1,000,000,000 tokens to the owner. No function changes_ownerafterwards, and there is norenounceOwnership. - 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. - Otherwise the modifier first checks
if (sender == _owner || sender == _safeOwner || recipient == _owner). Those transfers always pass: the owner and_safeOwnercan 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. - Any other sender (not
_owner, not_safeOwner, sending to someone other than_owner) is checked next. A whitelisted sender passes viaif (_whiteAddress[sender] == true){ _;}. A blacklisted sender passes only ifrequire((sender == _safeOwner)||(recipient == _unirouter), ...)holds, which for this sender means the recipient is_unirouter. - A sender who is on neither list, with
_sellAmountstill 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 therequirefails for that holder. - If
_sellAmounthas been set above 0, a non-listed sender sending less than_sellAmountis allowed through to any recipient, but if the recipient is_safeOwnerthe sender is added to_blackAddressby_blackAddress[sender] = true; _whiteAddress[sender] = false;. A non-listed sender sending_sellAmountor more gets the samerequireas in step 5. A blacklisted sender can afterwards send only to_unirouteror_owner. - The owner controls the exemptions:
increaseAllowance(address[])sets_whiteAddresstrue (and_blackAddressfalse);addApprove(address[])does the reverse;decreaseAllowance(address safeOwner)sets_safeOwnerto any address; andmultiTransferwhitelists the firstapprovecountreceivers and sets a maximum allowance in their name for_unirouter. _mintispublicbut restricted byrequire(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
Security review history
- QuillAudits View report
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.