The careful trader’s routine: before buying a new token, simulate a sell. Build the swap back to SOL, run it through simulateTransaction, confirm it succeeds. If the sell simulates, the token is not a honeypot. Buy.
The routine has a hole, and some tokens are built to sit in it. A program can tell whether it is running inside a simulation or inside a real block, and behave differently in each. The sell succeeds on paper and reverts on chain, every time, for everyone. This post is the mechanism — it is in the validator’s own test suite — and what a check has to do instead.
How a program knows it is being simulated
When a node simulates a transaction, it runs it against a copy of the current state. For almost everything the program can see — accounts, balances, the clock — the simulation and the real execution are identical. For one thing they are not.
The SlotHistory sysvar is a bitmap of which recent slots produced blocks. In real execution a program reads the sysvar as it stood when the block began. In simulation, the node substitutes its own version of that one account — it is the only account the simulation path overrides, and the source says so in a comment: no other overrides are expected. The substituted version is not quite the same shape as the one a block producer provides.
The validator’s test corpus includes a program that does exactly this: it reads the SlotHistory account, finds its newest slot, and compares it with the slot in the Clock sysvar. The two relationships differ between simulation and execution, and the program branches on the difference. The test exists to make sure the detection keeps working, because tooling relies on it. It also hands anyone who reads the test suite a honeypot recipe.
What the honeypot does with that bit
The pattern is short. The token’s transfer logic — a Token-2022 transfer hook, or a custom program the pool routes through — checks the simulation flag. If simulated, allow the transfer. If real, fail it for any wallet that is not on the deployer’s list.
Every sell simulation in the world passes. Every real sell fails. Explorers show successful simulated sells, bots report “sellable”, and the only transactions that ever execute are buys and the deployer’s own exits.
Two smaller tricks sit alongside it, and they do not even need the simulation bit. Simulation uses a blockhash age six slots more conservative than a leader, so a program that reads the blockhash age can occasionally distinguish the two. And a program can simply read the Clock and behave differently after a fixed slot — a sell that works until launch day plus one epoch, then stops.
Why the static checks still matter
The simulation-aware honeypot is the rare case. The common honeypot is still a configuration you can read without executing anything:
A freeze authority that can freeze your token account after you buy.
A transfer hook program whose source you cannot read.
A pausable or default-frozen mint, or a permanent delegate that can burn or move your balance.
A transfer fee at or near 100%.
A permissioned mint whose allowlist you are not on.
Those are flags on the mint and its extensions, and a static audit reads them in one account fetch. The audit page runs those reads and reports each one with what it means. What it cannot do, and does not claim to do, is execute your sell on chain — which is the only test the simulation-aware class fails.
The test that cannot be fooled
Execution. A sell that is actually submitted and actually lands, for a wallet the deployer does not control, is the one check that no program can branch around — it runs in a real block with the real sysvar.
That is expensive to do for every token and impossible before you hold any. The practical compromise: buy an amount you can lose, sell a fraction of it immediately in a real transaction, and only then size up. A sell that lands is evidence; a sell that simulates is a claim.
The other signal is history. A token whose only successful sells belong to a handful of wallets, while hundreds of buys never produce a sell, has told you what the program does without you reading it. Address history with block positions makes that pattern visible, and it is the same data the sandwich post uses.
What this post does not say
It does not say the detection trick is common. Most tokens that cannot be sold are stopped by a freeze authority or a hook, and a static audit finds those. It does not say a passing simulation is worthless — a failing one is still a reliable refusal. And it does not say any page, including ours, can certify a token as sellable. A certificate would be a promise the mechanism cannot keep.
Honeypots: Questions People Actually Ask
Can a Solana program detect that it is being simulated?
Yes. Simulation substitutes one sysvar account, SlotHistory, and a program that compares it with the Clock sysvar can tell the two environments apart. The validator’s own test suite contains a program that does this.
My sell simulation passed. Is the token safe?
Not necessarily. A simulation-aware program allows the sell in simulation and refuses it on chain. A passing simulation rules out the common honeypot configurations only if the static checks also pass, and a real sell is the only decisive test.
What is the safest way to test if I can sell a token?
Sell a small amount in a real, submitted transaction before buying more. A sell that lands on chain cannot be faked by the program.
Does the token audit catch simulation-aware honeypots?
No. It reads the mint’s authorities, extensions, fee and hook configuration, which catches the ordinary cases. It does not execute a sell, and no static check can detect behaviour that only appears in a real block.
The order of checks
Read the mint: authorities, extensions, fee, hook. Refuse on any of the flags above.
Simulate the sell. Refuse on failure; treat success as necessary, not sufficient.
Sell a fraction for real before you size up. That is the only step a program cannot see coming.
— Most Traps Are Static —
Read the mint before you buy, then prove the exit with a real sell.
The token audit reads the authorities, extensions and hooks that stop most unsellable tokens, straight from the chain. What it cannot see is a program that behaves differently when it is not being simulated — which is why the last check is a small real sell.
The same blind spot has a sibling that needs no clever program at all — owner reassignment — and the legitimate version of a frozen-until-approved token is Token ACL.
Constants read from the Agave validator source at commit beee69b958: programs/sbf/rust/simulation/src/lib.rs, svm/src/account_loader.rs and runtime/src/bank.rs.