Fei protocol Honeypot
What happened
De.Fi's REKT database lists a token named Fei protocol (TRIBE, Ethereum contract 0xd4f1aee8…5c7839) as an Exit Scam/Honeypot, noting that the contract owner could restrict holders from selling. The verified source routes every transfer through a burnTokenCheck modifier. Unless the sender is the owner, the tracked _safeOwner address or a whitelisted address, a transfer only succeeds when it goes to the hard-coded Uniswap V2 router address or the owner, so an ordinary sale into a pool reverts.
That router-or-owner rule holds while _sellAmount is 0; once the owner sets _sellAmount, non-whitelisted senders can also move amounts below it to any address. The owner can whitelist or blacklist addresses and reassign _safeOwner.
How it happened
- Initial state:
_safeOwner = owner;in the constructor,_sellAmountis 0, and_unirouteris hard-coded as 0x7a250d56…F2488D. - Every
transferandtransferFromruns_approveCheck(...)guarded byburnTokenCheck. The first branch isif (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}, so while_safeOwnerstill equals the owner, any transfer out of the owner passes and sets_safeOwnerto its recipient. Which address that is depends on the owner's first transfer, and no opened source shows it. An owner self-transfer in this state leaves_safeOwnerequal to the owner and does not set_sellAmount. - Otherwise
if (sender == _owner || sender == _safeOwner || recipient == _owner)lets the call through unconditionally: the owner and_safeOwnercan send anywhere, and anyone can send to the owner. Next,_whiteAddress[sender] == truelets whitelisted holders through. Both checks come before the blacklist test. - Everyone else is non-whitelisted and reaches the blacklist test and the
_sellAmounttest. With_sellAmountat 0,amount < _sellAmountis never true, so the sender hitsrequire((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");and can only send to the router address (or to the owner, via step 3). A normal sale moves tokens into a pool pair, which is neither, so it reverts with a misleading balance error. Whether the pool address equals_safeOwner, which would also let pool-to-buyer transfers through, is not shown by the code. - If the owner makes
_safeOwnerdiffer from_owner(via the first transfer ordecreaseAllowance) and then sends tokens to itself,_sellAmount = amount;is set. After that, a non-whitelisted, non-blacklisted sender can move any amount below_sellAmountto any address, and any such transfer to_safeOwnersets_blackAddress[sender] = true; _whiteAddress[sender] = false;. This blacklisting needs_safeOwner != _owner: a transfer to the owner is caught by step 3 first. Amounts at or above_sellAmountstill need the router as recipient. - A blacklisted sender takes the same
requireregardless of_sellAmount: it can send only to the router address, or to the owner (step 3 runs earlier), and nowhere else. Whitelisting removes the restriction entirely. - Owner-only functions manage this:
increaseAllowance(address[] receivers)whitelists and un-blacklists,addApprove(address[] receivers)blacklists and un-whitelists,decreaseAllowance(address safeOwner)sets_safeOwner, andmultiTransferwhitelists the firstapprovecountreceivers and gives the router a maximum allowance from them. No function changes_owner.
The code shows what the owner can do; no opened transaction shows any of these functions being called.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.