CFInu Honeypot
What happened
De.Fi's REKT database lists CFInu (Chinese Floki Inu) on BNB Chain as an exit scam/honeypot and describes the owner as able to disable the transfer function so that users could not sell. In the verified source, transferFrom runs a contextHandler modifier that lets the deployer-owner through and gates everyone else. A non-owner sender reverts outright when swapAndLiquifyEnabled is true, and otherwise only passes while the sender's whole balance is below _maxTrxLimit, a threshold the owner can change to any value with setMaxTrxLimit.
Plain transfer has no such check. The sources opened do not include a transaction for this token, so whether the owner changed these settings, held the liquidity or removed it is not established here.
How it happened
- The constructor sets
owner = msg.sender, starts withswapAndLiquifyEnabled = false, sets_maxTrxLimit = 50000 * 10**15;and gives the whole supply to the owner withbalances[owner] = _totalSupply;. With 8 decimals the limit is 5e11 tokens, 0.5% of the 1e14-token supply (10000000 * 10**15). - Two owner-only setters have no bounds:
function setSwapAndLiquifyEnabled(bool _swapAndLiquifyEnabled) onlyOwner publicandfunction setMaxTrxLimit(uint _input) onlyOwner public. Despite its name,renounceOwnership(address _newOwner)only writescurrentOwner, which no access check reads.onlyOwnertestsmsg.sender == owner || msg.sender == nxOwnerand no function assigns either, so control stays with the deployer. transferFromcarriescontextHandler(from, to). Whenfrom == owner, the modifier does nothing. Whenfrom != ownerandswapAndLiquifyEnabledis true,require(from == owner, "Order ContextHandler");always reverts. Whenfrom != ownerand it is false,require(balances[from] < _maxTrxLimit , "Order ContextHandler");compares the sender's entire balance, not the amount sent, so any holder with a balance at or above the limit cannot calltransferFrom.- Spending an allowance, which is how a router moves a holder's tokens in a sale, uses
transferFrom, so it hits this gate.transferhas no check, so direct wallet-to-wallet moves and transfers out of a pool are not gated by the modifier. - The owner can call
setMaxTrxLimit(0):balances[from] < 0is never true for auint, so every non-ownertransferFromreverts.setSwapAndLiquifyEnabled(true)has the same effect. The owner is exempt in both cases. The code allows this; no transaction opened here shows it was done. burnTokensdoes the opposite of its name:balances[msg.sender] = balances[msg.sender] + (_input);withemit Transfer(address(0), msg.sender, _input);adds any amount to the owner's balance and leaves_totalSupplyunchanged, so the owner can mint without limit. No transaction opened here shows it was used.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.