Formation Finance Honeypot

Reported loss Not disclosed
Ethereum
Honeypot

What happened

Formation Finance (FORM) is an Ethereum token contract deployed on 2021-06-03 by 0xF79891d6…99B3A3 in transaction 0x63438140…7ae230. De.Fi's REKT database lists it as an Exit Scam/Honeypot, noting that the contract owner could disable the transfer function, which restricted users in selling their tokens. In the verified source the restriction sits in a burnTokenCheck modifier on every transfer and transferFrom: with the default state, a holder who is not whitelisted can only send tokens to the hard-coded router address _unirouter (or to _owner), so a sale into a liquidity pair reverts, while owner-only functions can whitelist or blacklist addresses and re-point _safeOwner.

The code shows what the owner can do; no transaction showing these functions being called was opened for this entry.

How it happened

  1. The constructor stores _owner and _safeOwner as the same address and calls _mint five times. _mint credits _balances[_owner] regardless of its account argument, while the Transfer event names 0x98A8F350…9F8efC as recipient. _mint stays public and owner-only, so the owner can add to its own balance later.
  2. Both transfer and transferFrom call _approveCheck, which runs the burnTokenCheck modifier before moving balances.
  3. The modifier first checks if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}: while the two are equal, the owner's first outgoing transfer re-points _safeOwner to its recipient (a pair, if that transfer funds one). After that, any transfer where sender == _owner || sender == _safeOwner || recipient == _owner passes unconditionally, and an owner self-transfer sets _sellAmount = amount.
  4. A sender that is whitelisted (_whiteAddress[sender] == true) passes. A blacklisted sender hits require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"), so it can only send to _unirouter (_safeOwner was already handled above).
  5. Any other sender with _sellAmount at its default of 0 fails amount < _sellAmount and reaches the same require. A router sale calls transferFrom(holder, pair, amount), where the recipient is the pair, not _unirouter, so it reverts. Moving tokens to _unirouter or _owner still works.
  6. If _sellAmount is set above 0, a non-listed sender with amount < _sellAmount passes, and when recipient == _safeOwner that sender is moved to _blackAddress (_blackAddress[sender] = true), after which only sends to _unirouter pass.
  7. Owner-only functions manage the lists: increaseAllowance(address[]) whitelists, addApprove(address[]) blacklists, decreaseAllowance(address safeOwner) replaces _safeOwner, and multiTransfer whitelists the first approvecount receivers and approves _unirouter for the maximum allowance. There is no function that changes _owner.
  8. What the code allows is separate from what happened: De.Fi's REKT database lists the token as a honeypot, but no transaction showing the owner calling these functions or a failed sale was opened.

Protocol details

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

Build your security review skills

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