Goldfinch Finance Honeypot
What happened
De.Fi's REKT database lists Goldfinch Finance (GFF) on Ethereum as an exit scam / honeypot, stating that the contract owner could disable the transfer function and so restrict users from selling their tokens. The verified source for 0xd8eb25fb…a30ec4 has no single on/off switch of that name. It restricts transfers through the burnTokenCheck modifier: unless an address is whitelisted, a holder's transfer reverts unless it goes to the address stored in _unirouter, and only the owner can whitelist, blacklist, or change the exempt _safeOwner.
A swap-style sell, which sends tokens to a pair rather than to that address, is therefore blocked for ordinary holders by default. The listing gives no loss figure and no transaction, so what happened on the chain is taken from De.Fi's listing only.
How it happened
- The constructor stores
_owner = owner;and_safeOwner = owner;and then makes five_mint(..., initialSupply*(10**18))calls. Inside_mint, the line_balances[_owner] = _balances[_owner].add(amount);credits the owner regardless of theaccountargument, while theTransferevent names thataccount(0xa1D8d972…0a3fBf, 0xD551234A…2192FF, 0x2FAF487A…776AD2 and 0x32Be343B…102D88 for the four calls after the first)._mintispublicbut restricted byrequire(msg.sender == _owner, ...), so the owner can mint again later. - Every
transferandtransferFromruns_approveCheck, which carries theburnTokenCheckmodifier. Its first branch isif (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}: the recipient of the owner's first transfer becomes_safeOwner. If that first transfer is the liquidity deposit, the exchange pair becomes_safeOwner. - Privileged senders pass:
if (sender == _owner || sender == _safeOwner || recipient == _owner){ ... _;}. A transfer sent from the pair when it is_safeOwner(a buy) passes this way. A self-transfer by the owner also sets the sell threshold:if (sender == _owner && sender == recipient){_sellAmount = amount;}. - An ordinary holder (not whitelisted, not blacklisted) reaches
if (amount < _sellAmount){ ... }else{require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");_;}._sellAmountstarts at0, soamount < _sellAmountis false for every amount, and the holder's transfer reverts unless the recipient is the address stored in_unirouter(0x7a250d56…F2488D). A sell that moves tokens to the pair fails with the misleading message "transfer amount exceeds balance". - If the owner raises
_sellAmount, a holder who sells less than that amount to_safeOwneris let through but is marked byif(recipient == _safeOwner){_blackAddress[sender] = true; _whiteAddress[sender] = false;}. A blacklisted holder hitsrequire((sender == _safeOwner)||(recipient == _unirouter), ...)on every later transfer, so cannot sell again. - Only the owner can change the lists:
increaseAllowance(address[])sets_whiteAddress[...] = trueand_blackAddress[...] = false,addApprove(address[])does the reverse, anddecreaseAllowance(address safeOwner)sets_safeOwner.multiTransferwhitelists its firstapprovecountreceivers and approves_unirouterfor the maximum amount. The function names suggest ERC20 allowance helpers but they control the lists. - The source has no function that changes
_owner, so these powers stay with that address. The source shows these powers; it does not show which addresses were whitelisted, blacklisted or what_sellAmountwas. De.Fi's REKT database lists the token as an exit scam / honeypot.
Protocol details
Evidence
- report @Token_Sniffer incident report twitter.com
- address Etherscan verified contract code for 0xd8eb25fb9c1e51ee1e52919a29c438608ea30ec4 (ERC20, dibufs.sol; supplied locally from Sourcify) etherscan.io
- analysis Sourcify repository entry for chain 1, 0xd8eb25fb9c1e51ee1e52919a29c438608ea30ec4 repo.sourcify.dev
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.