Your wallet previews the swap, shows what you will receive, and reports no problem. You sign, and the transaction lands on chain as a failure that still charges you gas. The preview was not lying to you. It answered a question about one moment, and your transaction was executed in a different one.
What a simulation actually runs
A wallet preview is a dry run. A node executes your call against a copy of the chain state, reports what would happen, and then throws the result away. Ethereum's developer documentation describes the method behind it as one that executes a new message call immediately without creating a transaction on the blockchain, and describes the companion gas method as one that returns an estimate while the transaction will not be added to the blockchain.
Two properties follow from that description, and both matter later. The dry run is executed against a chosen block, so its answer is pinned to the state at that block. And it runs alone, with nothing else executing between the call and the result.
The state you simulated against is not the state you land in
Between the preview and the execution, your transaction has to travel. It is signed, broadcast, held in a mempool, selected by a block producer, and only then executed under the rules of the block that contains it. Every step of that trip takes time, and the chain does not pause while it happens.
Contract code reads state at execution time, never at simulation time. Pool reserves, an oracle answer, an allowance, a whitelist entry, a paused flag, a per-address cap, an auction that has ended: any of these can hold one value when the preview runs and another when the block is built. A contract that inspects such a value and stops when the condition is unmet is behaving identically in both moments. What changed is the input.
Minimum output and deadline: the checks that fail on purpose
A swap call can carry two guards inside the call itself. One is a floor on what you must receive, derived from your slippage tolerance. The other is a timestamp after which the call is no longer valid. Both are arguments you sign, so both are frozen at the values the preview computed.
Suppose the preview quotes 10,000 USDC for the tokens you are selling and your tolerance is set to 0.5%. The call then carries a floor of 9,950 USDC, and the contract is instructed to abandon the whole interaction rather than deliver less than that. When the price moves further than your tolerance while the transaction is in flight, the guard does exactly what you asked of it. The deadline behaves the same way: a call that sits unconfirmed past its timestamp is refused on arrival, even though the identical call would have gone through minutes earlier.
This is the case where the failure is the protection working. A guard that never triggers would let the interaction settle at whatever price the market had drifted to by the time a block was built.
Ordering: the same transaction in a different place
A block is a sequence, and transaction ordering decides which interaction sees which state. Your preview placed the call at the front of an empty queue. The block places it after whatever else the producer put there, and those neighbours can consume the liquidity, the allowance, or the remaining supply your call was counting on.
A mint with a hard cap shows this plainly. Ten wallets can each simulate the last available item successfully, because each preview runs against a state where that item is still unclaimed. One of them lands first and the other nine meet a sold-out contract. Nothing was wrong with the nine previews. They answered a question that had one answer before the block existed and a different answer afterwards.
Gas: an estimate is not a reservation
A gas estimate is produced the same way as the preview, by executing the call once and measuring it. The same documentation warns that the estimate may be significantly more than the amount of gas actually used, and the opposite direction is the one that hurts: an estimate measured on a cheap path can fall short of the path the transaction eventually takes.
Execution branches are where that gap opens. A swap routed through one pool at preview time may be routed through two at execution; a first write to a storage slot costs more than a later write to the same slot; a loop that touched three positions may touch nine. If the limit you signed runs out mid-execution, the work is undone and the gas is still consumed, which is the same arithmetic that applies to any failed transaction. Leaving headroom above the estimate does not raise the fee when the headroom goes unused, because how gas is priced separates the amount of work from the price per unit of work.
When the simulation was not simulating the transaction you sent
Sometimes the preview and the execution are not the same call at all. A simulation is performed against one endpoint on one network, so a wallet pointed at a different chain or at a stale node is answering about a different world from the one your signature is broadcast into.
Your own pending queue is a second source of the mismatch. Transactions from one account execute in nonce order, so an earlier pending call from the same wallet runs first and can change the state a later call depends on. When that earlier call is an approval that has not confirmed, the interaction queued behind it can be simulated against the allowance you expect and executed against the allowance you actually hold.
| What the preview showed | What changed by execution time | Where to look |
|---|---|---|
| A quoted output amount | Reserves or the oracle answer moved | The minimum output argument in the signed call |
| A valid call | The signed timestamp passed | The deadline argument and the time spent pending |
| Available supply or liquidity | Another transaction in the block took it first | The position of your transaction inside its block |
| A gas estimate | Execution took a longer branch | The gas used against the gas limit on the receipt |
| A clean dry run | The wallet was on another network or node | The chain identifier and endpoint used at signing |
What the failure looks like afterwards
An interaction that stops this way is recorded. It occupies a position in a block, it consumes gas, and its receipt carries a status field: EIP-658 replaced the intermediate state root of the receipt with a status code in which zero indicates failure and one indicates success. Explorers turn that field into the label reverted.
The label names the outcome and not the cause. Some contracts attach a reason string when they stop, and an explorer or a trace can surface it; others stop with nothing attached. Reading the failed transaction alongside the arguments you actually signed is what turns a status code into a diagnosis, because those arguments are the part of the story the receipt cannot reconstruct for you.
What actually lowers the failure rate
Shorten the gap. A preview taken a moment before signing describes a state closer to the one the block will hold, and a transaction that confirms quickly has less time in which to be overtaken.
Size the guards to the asset rather than to habit. A floor tight enough to reject an ordinary minute of movement in a thin market will keep stopping your interactions, while a floor loose enough to accept anything gives up the protection you set it for. The same judgement applies to the deadline, which has to be long enough to survive a period of congestion.
Treat a repeated failure as information. When the same interaction stops several times with the same arguments, the contract is reporting a condition that cannot currently be satisfied, and resubmitting the identical call spends gas to receive the identical answer.
The bottom line
A simulation answers what would happen if this call ran now, alone, against this block. An on-chain execution answers what happened when the call ran later, among other transactions, against a different block. A failure after a clean preview is the distance between those two questions, and the guards you signed are what convert that distance into a stop rather than a bad fill. Check the arguments in the signed call, the position of the transaction inside its block, and the gas used against the gas limit, and the reason is in one of the three. To keep learning the fundamentals, follow more from Bitbase Academy.
Related reading
Other Bitbase articles on this topic:
- How to Switch RPC Endpoints Safely
- Maximum Transaction Fee Exceeded: What That Wallet Warning Means
- RPC Rate Limit Exceeded and How to Stop Hitting It
- FIFO vs LIFO for Crypto Cost Basis
- Memetic Premium: Why Memes Move Prices
Disclaimer: This article is educational content from Bitbase Academy, provided for information only. It does not constitute investment, trading, tax, or financial advice. Crypto assets are volatile; assess your own risk. Written as of September 2026; refer to the latest official information.
References
[1] Ethereum.org Developer Documentation, JSON-RPC API (eth_call, eth_estimateGas) ethereum.org
[2] Ethereum Improvement Proposals, EIP-658: Embedding transaction status code in receipts eips.ethereum.org
[3] Ethereum.org Developer Documentation, Transactions ethereum.org






