Winnie-the-Pot Honeypot
What happened
Winnie-the-Pot (WTP) is a BNB Chain token whose verified source can reject transfers to one stored address, nxOwner. While the tracing flag is true, transferFrom records the recipient of the first call (with a non-zero from) in nxOwner, and from then on both transferFrom and transfer revert when the recipient is that address. A DEX router sells a holder's tokens into the pair with transferFrom, so if the first recorded recipient is the liquidity pair, sells into it revert while transfers to other addresses, including the pair paying out a buyer, are not blocked by this check.
The owner can flip tracing with setTracing. De.Fi's REKT database lists the token as an Exit Scam/Honeypot dated 2021-05-12 and says the owner could disable the transfer function, which restricted users in selling their tokens; the listing gives no loss figure. The code blocks one recipient rather than the whole transfer function, and no transaction was opened to show which address nxOwner holds.
How it happened
- The constructor sets
symbol = "WTP",decimals = 9, credits_totalSupply = 10000000000000000000000(raw units) to the deployer withbalances[owner] = _totalSupply;and sends half of it to the dead address withtransfer(dead, burnedAmount);.tracingstarts astrueandnxOwneris empty. - The first
transferFromwhosefromis non-zero whilenxOwneris still empty succeeds and stores its recipient:if(from != address(0) && nxOwner == address(0)) nxOwner = to;. The code does not say who that recipient is; in a router-based liquidity add it would be the pair. - After that, while
tracingistrue, everytransferFromrunselse require(to != nxOwner, "Order ContextHandler");and everytransferrunsrequire(to != nxOwner, "Handling Request");. A holder's sell routed to the stored address therefore reverts regardless of the holder's balance. setTracing(bool _tracing) onlyOwner publicwrites the flag, so the owner can lift or re-impose the block. TheonlyOwnermodifier isrequire(msg.sender == owner || msg.sender == nxOwner);, so the captured address also passes it.renounceOwnership(address _newOwner)has no access check and only writescurrentOwner; nothing in the code changesowner, so it does not give up any power.emitTransfer(uint256 _input) onlyOwneradds_inputto the caller's balance without changing_totalSupply.
Protocol details
Evidence
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.