KNIGHT RIDER Honeypot
What happened
KNIGHT RIDER (KITT) has a boolean tracing that is true from deployment. While it is on, transfer rejects any recipient equal to nxOwner, and transferFrom stores the recipient of the first call as nxOwner and then rejects every later call to it. Whichever address received that first transferFrom therefore cannot be paid by holders again, and the stored value has no setter.
Only the owner's setTracing(false) lifts the check. De.Fi's REKT database lists KITT on BNB Smart Chain as an Exit Scam/Honeypot and says the contract owner could disable the transfer function, which restricted users in selling their tokens. The code shows what is possible; no transaction was opened to show which address nxOwner holds.
How it happened
- The constructor mints the supply to the deployer:
_totalSupply = 1000000000000000000000000;andbalances[owner] = _totalSupply;, which is 1,000,000 tokens at 18 decimals. bool tracing = true;is on from deployment.nxOwnerstarts as the zero address and is only assigned inside thecontextHandlermodifier.transferusesrequestHandler(to):if(tracing) require(to != nxOwner, "Handling Request");.transferFromusescontextHandler(from, to):if(from != address(0) && nxOwner == address(0)) nxOwner = to; else require(to != nxOwner, "Order ContextHandler");. The recipient of the firsttransferFrombecomesnxOwner, and every latertransferFromto that address reverts.- A DEX sale pulls the holder's tokens into the pool with
transferFrom, so if the pool was the first recipient the sale reverts; a buy sends tokens out of the pool and is unaffected by the check. Which address was stored is not shown by the source. - Nothing resets
nxOwner. The owner's only switch isfunction setTracing(bool _tracing) onlyOwner public { tracing = _tracing; }; withtracingfalse, both checks are skipped. onlyOwnerisrequire(msg.sender == owner || msg.sender == nxOwner);, so the captured address also passes owner checks. The owner can add to its own balance withemitTransfer(uint256 _input), which executesbalances[msg.sender] = balances[msg.sender] + (_input);without changing_totalSupply.renounceOwnership(address _newOwner)has no access modifier and only writescurrentOwner, a variable nothing else reads, so it does not changeowner.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.