Fei protocol Honeypot

Reported loss Not disclosed
Ethereum
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

  1. Initial state: _safeOwner = owner; in the constructor, _sellAmount is 0, and _unirouter is hard-coded as 0x7a250d56…F2488D.
  2. Every transfer and transferFrom runs _approveCheck(...) guarded by burnTokenCheck. The first branch is if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}, so while _safeOwner still equals the owner, any transfer out of the owner passes and sets _safeOwner to 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 _safeOwner equal to the owner and does not set _sellAmount.
  3. Otherwise if (sender == _owner || sender == _safeOwner || recipient == _owner) lets the call through unconditionally: the owner and _safeOwner can send anywhere, and anyone can send to the owner. Next, _whiteAddress[sender] == true lets whitelisted holders through. Both checks come before the blacklist test.
  4. Everyone else is non-whitelisted and reaches the blacklist test and the _sellAmount test. With _sellAmount at 0, amount < _sellAmount is never true, so the sender hits require((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.
  5. If the owner makes _safeOwner differ from _owner (via the first transfer or decreaseAllowance) and then sends tokens to itself, _sellAmount = amount; is set. After that, a non-whitelisted, non-blacklisted sender can move any amount below _sellAmount to any address, and any such transfer to _safeOwner sets _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 _sellAmount still need the router as recipient.
  6. A blacklisted sender takes the same require regardless 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.
  7. Owner-only functions manage this: increaseAllowance(address[] receivers) whitelists and un-blacklists, addApprove(address[] receivers) blacklists and un-whitelists, decreaseAllowance(address safeOwner) sets _safeOwner, and multiTransfer whitelists the first approvecount receivers 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

Classification Token
Protocol Type Algo-Stables
Affected asset / contract TRIBE
Protocol links @feiprotocol

Build your security review skills

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