Honeypot tokens look fine when you buy, then trap you when you try to sell. The core trick lives in the token’s sell path, not the buy path. On EVM chains like BNB Smart Chain and Ethereum, most honeypots gate the transfer or transferFrom logic based on who is sending, who the recipient is, and which pool address is involved. The code sits in the token or in a helper contract the token calls during transfers. If you only skim the verified source, you will miss a lot. Bytecode, control flow, and selector fingerprints tell the real story.
This guide walks through how I detect honeypots using bytecode patterns, with a practical mindset. I will keep the math behind it invisible and focus on the checks that work when you are staring at Etherscan or BscScan, or when you script a scanner.
What a honeypot looks like on-chain
A honeypot often lets buys through but systematically reverts sells to the liquidity pool. That can mean a hard revert, a return of false that bubbles up, or a condition that sets a 100 percent fee to burn or dev address, making the effective sell value zero. On PancakeSwap or Uniswap, the sell path is any transfer where to equals the pair address or the router, depending on how they implemented swaps.
Watch for tokens where buys succeed, chart candles are only green, and slippage keeps rising even with healthy liquidity. If you try a tiny test sell and it fails with a generic "Transfer failed" or the pool never receives tokens, you are likely in honeypot territory. DexScreener will often show only buy volume spikes with no matching sell pressure. That shape is a red flag.
Why bytecode patterns beat source code
Scammers love verification theater. They upload a clean ERC-20 and then point your wallet at a different contract via a proxy. Or they verify a partial build while deploying a slightly changed bytecode. Sometimes the sell filter lives in a separate helper contract reached via delegatecall during transfer.
Bytecode analysis cuts through that. The runtime bytecode is what the EVM executes. If you hash it, decompile it, or even skim the opcodes around transfer selector dispatch, you see the truth. Source code can be hidden, proxies can swap logic, but the runtime bytecode on the implementation contract is final until upgraded.
Where the sell restriction usually hides
The sell restriction almost always sits in _transfer or in the function that both transfer (0xa9059cbb) and transferFrom (0x23b872dd) call. You can confirm the path by finding both selectors and following the internal jump to a shared routine. In bytecode, that shared block gates execution with conditionals before SSTORE updates balances.
Here are recurring tricks I keep seeing in audits and forensics on BSC and Ethereum:
- Whitelist or blacklist checks keyed by msg.sender, from, or to. These often compare against the pair or router. In opcodes, you will see EQ, conditional JUMPI, and storage reads keyed by hashed addresses, for example keccak256(0x00, addr). MaxTx or maxWallet limits enforced only when to equals the pair, which silently blocks sells while allowing buys. Patterns read SLOAD of a max threshold, then revert when sending to the pair. Trading open flag with owner bypass. They set tradingOpen = false at launch, open buys by whitelisting the pair or router, then never flip it for retail sellers. In bytecode, it is a single SLOAD gated by CALLER equals owner or a hardcoded dev wallet. Dynamic fee path that spikes tax to 100 percent only on sells. You will see a branch that sets fee = amount when to equals pair and from not whitelisted. External honeypot helper. The token makes a STATICCALL or CALL during transfer to a separate contract that returns whether to revert or tax max. Bytecode flags include pushing a fixed address followed by STATICCALL, then branching on the result.
If you want a mental model, look for conditional guards keyed to the pair or router, and for branches that only activate when tokens flow into the pool.
Reading bytecode without losing your weekend
You do not need to be a decompiler wizard. The fastest path is to anchor on three things: function selector switches, pair address discovery, and storage gates.
First, check the dispatch. At the top of runtime bytecode, find the jump table that routes by msg.sig. You should see constants like 0xa9059cbb for transfer and 0x23b872dd for transferFrom. Trace where each jumps. In many ERC-20s they land in the same handler.
Second, identify how the contract knows the pair. Tokens either store a pair in storage during constructor or compute it via the factory. In bytecode, pair constants often appear as:
- Hardcoded factory addresses like Pancake Factory on BSC. The CREATE2 salt formula for UniswapV2 pairs. Calls to getPair on a factory via STATICCALL.
When the pair address is static in storage, you will see a write in the constructor, then repeated reads during transfers.
Third, look for gates around sells. That means a compare where to == pair or from == pair, followed by a conditional block that sets fee, checks tradingOpen, or reads a blacklist mapping. In opcodes, a typical shape is PUSH20 DUPx EQ ISZERO ISZERO JUMPI then a subroutine that leads to REVERT or to computing fee = amount.
You can spot fee hijacks by an assignment where the post-fee amount becomes zero when to equals pair. The code computes amount * fee / denominator and then later writes zero for the final transfer, or it routes the entire amount to a dev wallet address during a sell branch.
Concrete bytecode fingerprints I trust
A few signatures keep paying dividends:
- Selector filter on router or pair. If the code does CALLER compare against router or pair, and that drives a path to set tradingOpen, it is often misdirection. The real block is a compare on to == pair within the internal transfer. Hidden blacklist mapping. Look for keccak256(addr, slotIndex) style mapping reads with addresses derived from CALLER, from, to, or tx.origin. If any of those branches to REVERT only when to equals pair, that is a honeypot. External checker during transfer. Any STATICCALL inside transfer that returns a boolean and then gates the move is high risk. If the target address is owner controlled, that is a programmable trap. Pair-liveness checks. Some tokens STATICCALL getReserves on the pair each transfer. If the call fails or a reserve condition is unusual, they revert sells to avoid price impact. Sounds legit, but legit tokens do not revert just because of pool state. Gas grief or assembly require. A manual require(gasleft() > X) branch that triggers only on sells. Real tokens avoid gas-based gates.
Tools like Etherscan’s disassembler or open-source EVM decompilers make this faster. If you prefer visual flow, a control flow graph helps spot the sell-only branch that ends in a revert.
A practical bytecode workflow for a honeypot token checker
- Pull runtime bytecode from Etherscan or BscScan. If it is a proxy, resolve the implementation from the storage slots defined by EIP-1967 or transparent proxy patterns, then fetch that bytecode. Extract function selector table. Confirm transfer and transferFrom presence, map their jumps, and locate the shared internal transfer. Identify the pair and router references. Scan for known factory addresses and the UniswapV2 pairFor CREATE2 pattern. Cache the pair address if it is written to storage. Search for conditional branches on to == pair or from == pair. Track what happens inside the branch. Flag any path that sets fee to 100 percent, reads a blacklist mapping, calls external authorizer, or ends with REVERT. Simulate a sell. With a lightweight EVM, set from to a random EOAs, to to the pair, and nonzero balances. Run transfer to the pair and inspect whether the call reverts or routes funds to a third address. This catches obfuscated branches that static pattern matching might miss.
This mix of static patterns plus a cheap simulation makes a stable honeypot token checker. It is not fancy, just practical.
Source-level hints still worth checking
Even if bytecode is king, I still scan source when verified. Early traps include:
- setFeeTx, setTax, setMaxTx, setMaxWallet functions callable by owner after launch. If any owner-only function changes sell behavior, you have mutable risk even if not a honeypot today. Fake renounceOwnership. Some use Ownable2Step or custom ownership that keeps upgrade power via a proxy or a role like operator. On BscScan, click through “Read/Write as Proxy” if the contract shows a proxy banner. Blacklist or whitelist functions exist but are “disabled” by a flag. If the flag can be turned back on, assume it will be. Transfer delay or cooldown that triggers only on sells. Reflection tokens used this for bots, but scammers abuse it to block sells.
Firms like PeckShield, CertiK, Hacken, and ConsenSys repeatedly warn about mutable tax and blacklist controls. Those do not automatically mean scam, but they make it easy to switch into honeypot mode.
Evasion tricks and how to counter them
Scammers adapt. They know basic honeypot scanners call swapExactTokensForETH and look for a revert. So they let small sells pass, then trap larger sells, or they gate only through transferFrom called by the router. Your checker should simulate both direct transfer to the pair and a router call path.
They also hide logic in an external checker contract. If the token calls STATICCALL to an address that is not a well-known anti-bot service, assume the risk is high. Cache that address and inspect its bytecode too. If it is upgradable or deploys via CREATE2 per launch, flag it.
Some use per-block or per-timestamp windows. If your simulation uses a single block timestamp, you might get a false negative. Randomize a small time window in tests and vary gas limits to smoke out gas-based gates.
Finally, router spoofing is common. Tokens store multiple router addresses and treat only one as “safe,” while retail uses another. Pattern match all router constants, not just the main Pancake or Uniswap router.
Reality check with market data and explorers
I always triangulate bytecode findings with live on-chain behavior. On DexScreener, look for green-only charts. On CoinGecko, listings without CEX price references and with thin liquidity are more likely to be traps. On Etherscan or BscScan, inspect the “Holders” tab. If the top holder is the token itself, or the deployer controls the LP tokens, danger rises.
A quick “Internal Txns” view during fresh learn more buys can reveal where fees route. If large fees go to a wallet that then bridges out or splits across fresh wallets, that is classic extraction. Many on-chain sleuths on X call these out fast. When PeckShield alerts a suspicious deploy cluster on BSC, I take it seriously.

The two-minute manual checklist before you buy
- Check BscScan or Etherscan for proxy patterns, owner powers, and verified code that matches bytecode size. If proxy, open the implementation. In “Read Contract,” fetch pair, router, tradingOpen, tax values, and blacklist flags. If there is any blacklist setter live, treat as unsafe. Try a dust buy with a controlled wallet, then a tiny sell within a minute. Note the revert messages and gas consumed. Watch DexScreener for sell prints. If 10 minutes pass with only buys on a fresh launch, assume a block is active. Inspect LP token ownership. If LP tokens are not burned or locked, the seller can rug, even if not a honeypot.
Building a safer honeypot token checker
- Collect known router and factory addresses per chain, plus CREATE2 pair formulas, so you can auto-detect pairs in bytecode without relying on storage reads. Precompute critical selectors like transfer, transferFrom, approve, owner, and expected fee setters. Map them to jump destinations and detect a shared transfer core. Implement a tiny EVM to run transfer with custom from, to, and pair scenarios. Log reverts, fee outcomes, and external calls during the sell branch. Flag external calls in transfer. If the callee is not a common router or pair, and it returns a single boolean that gates flow, score it high risk. Add heuristics for 100 percent tax, per-address gating, and owner-only sell path bypass. Weight them into a confidence score to avoid false positives on legit anti-bot windows.
A safe token scanner that marries pattern recognition with minimal simulation beats most public honeypot checkers. Keep it explainable. When you mark a token risky, show which branch, which storage slot, and which comparison caused the flag.
Edge cases worth respecting
Some fair launches add a brief anti-bot phase with per-block limits, which can look like a honeypot for the first few minutes. Pinksale projects often enable trading in stages. Test again 10 to 15 minutes later and see if sells clear. Also, a few reflective tokens route fees on both buys and sells but do not block exits. Your simulator should differentiate revert from high tax.
On the flip side, “renounced” ownership is often cosmetic. Many tokens rely on a role-based access manager or a second upgrade key. Even if sells are open at launch, a later upgrade can flip to honeypot. Track upgradeable implementations and monitor for implementation changes.
Bottom line
If you learn to read runtime bytecode for sell-only branches, you will catch most honeypots before you touch the buy button. Focus on where to equals the pair, trace the branch, and ask a simple question: can a normal EOA transfer to the pool without owner blessings, external approvals, or a 100 percent tax? If the answer is no, walk.
Security firms like PeckShield, CertiK, Hacken, and ConsenSys have hammered this point for years, and on-chain sleuths prove it daily. Your best defense is a short bytecode habit plus a quick live test. The moment you see a token cannot sell, treat it like fire. That habit alone will help you avoid crypto scams far more effectively than any shiny marketing promises about anti-bot magic.