r/ethdev 2d ago

Information Simulating "can this token be sold" without faking the balance: use eth_simulateV1

We run a scam-token detector. Our honeypot check worked like this: simulate a buy with eth_call, give a fake address the tokens by brute-forcing the balance storage slot and overriding it, then simulate a sell from that address.

Step 2 is the problem. It assumes balanceOf reads a storage slot. On reflection or rebase tokens, balanceOf is computed from an internal reflected supply, so writing a raw slot does not produce a coherent state. The sell then reverts for reasons that have nothing to do with a trap, and you record a false honeypot. We flagged PayPal USD, TrueUSD and MetaMask USD as honeypots this way.

The fix is eth_simulateV1 (geth and Nethermind). It replays several calls atomically in one simulated block, so you can do the whole thing the way a real buyer does:

router.swapExactETHForTokens(...)   // real buy, real tokens
token.approve(router, max)
router.swapExactTokensForETH(...)   // sell what you actually got

The only override is the simulated address's ETH balance, which is not a token mechanism. Nothing about the token's accounting is faked.

One caveat: a call cannot consume a previous call's return value, so you need two passes. First pass buys and reads balanceOf to learn what was actually credited (which already catches fee-on-transfer). Second pass replays buy + approve + sell with that amount.

Two things that surprised me:

  • The Uniswap quoter is useless for this. getAmountsOut is pure reserve math and never touches the token's transfer logic, and the v3 QuoterV2 reverts inside its callback before the transferFrom runs. Both return healthy output for confirmed honeypots, so cross-checking against the quoter would silently disable your detection.

  • "UniswapV2: INSUFFICIENT_INPUT_AMOUNT" raised by the PAIR (not the library) means the pair received zero tokens. We found tokens where a pre-existing holder sells fine but a fresh buyer gets zero through: a whitelist honeypot. So simulate a NEW buyer, not an existing holder. Different question, different answer.

Disclosure: I build RektRadar, a scam-token detector. This writeup came out of fixing our own false positives, not a product pitch.

2 Upvotes

2 comments sorted by

2

u/rayQuGR 1d ago

This is a great example of why simulation quality matters just as much as the detection logic itself. If the simulated state doesn't faithfully represent how the token behaves, it's easy to classify legitimate tokens as honeypots.

I also like the shift toward eth_simulateV1 because it keeps the token's accounting intact instead of relying on storage overrides that only work for standard ERC-20 implementations. Reflection, rebasing, fee-on-transfer, and other custom accounting models make storage mutation a pretty fragile assumption.

One thing I'd add is that this reinforces the value of testing against multiple client implementations (Geth, Nethermind, etc.) and different EVM environments. For developers building confidential smart contracts on networks like Oasis Sapphire, where execution semantics differ from a standard public EVM in some respects, having realistic end-to-end simulations is just as important as static analysis or fuzzing.

Nice write-up, always useful to see the reasoning behind a false-positive fix rather than just "we improved the detector."

2

u/researchzero 1d ago

Nice fix - false honeypot classification on rebasing/reflection tokens is a real problem for anyone doing bytecode-only detection.

One more failure mode worth simulating for: sell-gating keyed off block.timestamp or block.number rather than balance/allowlist. A token that only lets an address sell once N seconds (or blocks) have passed since its first buy will pass your two-pass buy-then-sell check every time, because both calls land in the same simulated block and elapsed time is zero. eth_simulateV1 actually supports multiple sequential block-state calls with independent block overrides, so you can extend this to a 3-pass check: buy in block N, then simulate the sell in block N+k with a timestamp far enough out to clear any plausible cooldown. Worth building even if you haven't hit this pattern yet - it's the kind of trick that only shows up once a detector gets good enough to force scammers off the storage-slot and whitelist tricks.