simulate_before_send — Check Before You Send

There is a moment, right before clicking "Submit," when you wonder: did I check everything? The form is filled out. The fields look right. The attachments are attached. But there is a difference between looking right and being right. And once you click Submit, the distinction stops being philosophical and starts being consequential.

In Solana MEV, that moment comes right before sending a bundle to the block engine. The transaction is built. The instructions are assembled. The accounts are referenced. The tip is included. Everything looks right. The question is whether to send it immediately — trusting the math, trusting the data, trusting the pipeline — or to pause for one additional step. Run the transaction through simulateTransaction first. See if it actually works. Then decide whether to send it.

This is the question of pre-simulation: is it worth checking your work before submitting it?

The Practice Test

Americans grow up with a particular ritual around standardized testing. Before the SAT, before the ACT, before any exam that matters, there is the practice test. You sit down with a timer, work through the questions, check your answers, and identify the gaps. The practice test does not count. It does not go on your transcript. No college sees the results. Its only purpose is to show you what would happen if this were the real thing.

The practice test takes time. Two hours, three hours, sometimes an entire Saturday morning. That time could be spent studying. It could be spent reviewing flashcards or reading the textbook one more time. The practice test is not learning — it is verification. It tells you whether the learning worked. And there is a cost to that verification: the hours spent taking the practice test are hours not spent on other preparation.

But anyone who has taken a standardized test knows the value. The practice test reveals the mistakes you do not know you are making. The question you were sure about turns out to be wrong. The formula you memorized has a sign error. The reading comprehension passage you breezed through had a trick in the third paragraph. The practice test catches these things before the real test, when catching them still matters, when you can still fix the problem.

simulateTransaction is the practice test for a Solana transaction. It takes the signed transaction, runs it against the current on-chain state, and returns the result — success or failure, error codes, program logs, compute units consumed — without committing anything to the chain. It is the practice run before the real run. The dry fire before the live round. The dress rehearsal before opening night.

How It Works

The mechanics are straightforward. simulateTransaction is a standard Solana RPC method. Any public RPC endpoint supports it. You send a serialized transaction, and the RPC node executes it against its current view of the chain state. The node runs every instruction, invokes every program, accesses every account, performs every calculation — exactly as it would during real execution. The only difference is that nothing is committed. The results are thrown away after being reported back. The chain state is unchanged. No fees are charged. No accounts are modified. No programs' state is altered.

The response contains everything you need to evaluate the transaction's viability. If the transaction succeeds, the simulation returns success along with the compute units consumed and any program logs generated during execution. If the transaction fails, the simulation returns the specific error — the instruction index that failed, the error code returned by the program, the program logs leading up to the failure. It is a complete execution report for an execution that never happened.

This is different from the bundle status API, which returns a single word — Landed, Failed, Invalid, Dropped — with no diagnostic detail. The simulation response is rich. It is specific. It points to the exact instruction, the exact program, the exact error. If a transaction is going to fail, the simulation tells you why, not just that.

The Case for Checking

The argument for pre-simulation is intuitive. Why send something that is going to fail?

Every bundle that fails on-chain is wasted. The time spent building it is wasted. The RPC calls used to fetch account data are wasted. The compute resources consumed by the block engine to process and execute the failing bundle are wasted. The tip — if the bundle somehow lands despite the failure, or if the block engine processes tips differently than expected — is wasted. Every failing bundle is a cost with zero return.

Pre-simulation is a filter. It catches the bundles that are already broken — the ones that will fail regardless of timing, regardless of competition, regardless of state changes. If the profit check in the on-chain program is going to revert the transaction because the arbitrage margin is too thin, the simulation catches that. If a CPI call to a DEX program is going to fail because the pool parameters have changed, the simulation catches that. If a compute budget is going to be exceeded because the transaction's instruction set is too expensive, the simulation catches that.

These are not timing-dependent failures. These are not failures caused by state changes between submission and execution. These are failures that exist in the transaction right now, at this moment, against the current state. The transaction is broken. Sending it will not un-break it. The simulation identifies the breakage before the bundle enters the auction pipeline, saving the submission cost and — more importantly — saving the time that would otherwise be spent waiting for a FAILED status and wondering why.

Think of it like checking a recipe before you start cooking. You read through the ingredients. You check the pantry. You confirm you have eggs, flour, butter, sugar. If you are missing the eggs, you know before you start mixing. You do not preheat the oven, measure the flour, cream the butter and sugar, and then discover — three steps in, with a half-mixed batter sitting on the counter — that you are out of eggs. The check takes thirty seconds. Skipping the check wastes thirty minutes and a bowl of unusable batter.

Pre-simulation is that thirty-second check. Look before you leap. Verify before you send.

The Case Against Checking

The argument against pre-simulation is equally intuitive, and it is about time.

A simulation takes time. The transaction must be serialized and sent to an RPC node. The RPC node must receive it, deserialize it, load the relevant accounts, execute the instructions, collect the results, and send the response back. This is not instantaneous. It is a network round trip plus execution time. Depending on the RPC provider, the network conditions, and the transaction's complexity, this takes somewhere in the range of tens to hundreds of milliseconds.

In MEV, milliseconds are the competition. The difference between landing a bundle and missing the opportunity is often measured in single-digit milliseconds. Two bots detect the same opportunity at nearly the same time. Both build transactions. Both prepare bundles. The one that submits first has an advantage — not a guarantee, but an advantage. Every millisecond of delay between opportunity detection and bundle submission is a millisecond in which a competitor can get their bundle in first.

Pre-simulation adds a step to the pipeline. It inserts a network round trip between "transaction ready" and "bundle submitted." The bot that simulates before sending is the student who takes the practice test before the exam. The bot that sends immediately is the student who walks straight into the testing center and starts answering questions. The second student might make more mistakes — but the second student also starts the exam earlier. And in an exam where the first person to submit a correct answer wins, starting earlier matters.

This is the DMV problem. You can check your paperwork before you get in line — verify that you have the right forms, the right ID, the right proof of address. That takes ten minutes. Or you can get in line immediately and hope everything is in order. If your paperwork is wrong, you waste the time you spent waiting in line. But if your paperwork is right — and it usually is, because you have done this before — you just saved ten minutes of checking that turned out to be unnecessary. The line at the DMV is long. The people who skip the pre-check and jump straight in line sometimes finish first.

The MEV environment makes this tradeoff sharper than it is in everyday life. In everyday life, the cost of checking is small relative to the cost of failure. Checking your recipe takes thirty seconds; a failed cake wastes an hour. Checking your DMV paperwork takes ten minutes; a rejected application wastes two hours. The ratios favor checking.

In MEV, the cost of checking is large relative to the window of opportunity. The simulation takes, say, fifty milliseconds. The entire lifespan of the opportunity might be two hundred milliseconds. Spending fifty milliseconds on verification means spending a quarter of the available window on a check that might be unnecessary. If the transaction is valid — and most well-built transactions are — the fifty milliseconds were spent confirming something that did not need to be confirmed, while the opportunity aged by fifty milliseconds.

The State Gap Problem

There is a deeper problem with pre-simulation that goes beyond latency. It is the state gap.

When the simulation runs, it executes the transaction against the on-chain state at time T. When the bundle eventually executes on-chain — if it lands — it executes against the on-chain state at time T + delta, where delta is the time between simulation and execution. In that delta, the state can change. Other transactions land. Pool reserves shift. Prices move. Balances change. The state that the simulation tested against and the state that the real execution encounters might be different.

This means a simulation success does not guarantee an execution success. The simulation says "this transaction works against the current state." The real execution says "this transaction must work against the state that exists when I actually run, which might be different from the state that existed when you simulated." If the state changes in between — if another bot's transaction lands and alters the pool reserves — the simulation's verdict is obsolete. It tested against a state that no longer exists.

The reverse is also true, though less practically relevant: a simulation failure does not guarantee an execution failure. The simulation might fail because the pool state at time T makes the transaction unprofitable. But by time T + delta, another transaction might have moved the state in a direction that makes the transaction profitable again. The simulation says "fail" and the bot discards the bundle — but if the bundle had been sent anyway, it might have landed successfully.

This is like the dress rehearsal problem in theater. The dress rehearsal is performed on Wednesday night. Opening night is Thursday. The actors run through the entire show. Act Two has a lighting cue that does not fire. The stage manager notes it, fixes it, and the show goes on. But what if the lighting board develops a different problem between Wednesday and Thursday? The dress rehearsal caught Wednesday's problem, but Thursday might have its own problems. And what if the missed cue on Wednesday was caused by a loose cable that someone kicks back into place overnight? The problem that existed during rehearsal does not exist during the show. The rehearsal was not wrong — it accurately reflected the state of the system at the time — but it was not a perfect predictor of the future state.

The simulation is always accurate for the moment it runs. It is never guaranteed to be accurate for the moment the transaction actually executes. The gap between those two moments is the irreducible uncertainty of pre-simulation. It cannot be eliminated. It can only be minimized — by running the simulation as close to the submission time as possible, by keeping the pipeline tight, by reducing the delta between simulation and execution to the smallest achievable value.

The Spectrum of Strategies

The question is not simply "simulate or not." There is a spectrum, and the optimal point on that spectrum depends on circumstances.

At one extreme: simulate everything. Every transaction, before every submission, gets a simulation pass. Nothing enters the bundle without a passing simulation. This maximizes quality. The bundle submission rate drops — you send fewer bundles — but the bundles you send are pre-validated. The failing garbage is filtered out. The bundles that reach the block engine are clean.

The cost is latency on every single submission. Every opportunity pays the simulation tax, including the ones that were going to succeed anyway. For a bot that submits hundreds of bundles per minute, adding even fifty milliseconds to each submission means adding seconds of cumulative delay per minute. Some of those delays cost opportunities. Some of them do not. But you pay the cost on every submission regardless.

At the other extreme: simulate nothing. Build the transaction, assemble the bundle, send it immediately. Skip the check entirely. Trust the math. Trust the data. Trust the pipeline. If the bundle fails, it fails. The block engine will tell you — eventually, in its one-word vocabulary. Deal with the failure after the fact. But get the bundle in the door as fast as possible.

The cost is failure rate. Without pre-simulation, bundles that are already broken enter the auction pipeline. They consume block engine resources. They come back FAILED. They waste the submission. If the bot's transaction construction pipeline has bugs, or if the data it uses is systematically stale, or if the profit estimation is systematically optimistic, the failure rate can be high. Every failed bundle is a wasted submission in a system where you are competing for limited slots.

Between these extremes lies a range of options. Simulate selectively. Simulate based on risk assessment. Simulate the transactions that are more likely to fail and skip simulation for the ones that are more likely to succeed. Simulate when the market is calm and skip simulation when the market is volatile. Simulate new pool interactions and skip simulation for well-known pools with established patterns.

This is how a cook decides whether to check a recipe. Making your grandmother's chocolate chip cookies for the hundredth time? You do not need to check the recipe. You know it by heart. The ingredients are in your hands before you consciously think about them. But making a souffle for the first time? You check the recipe. You check it twice. You check it at every step. The familiarity of the task determines the necessity of the verification.

What Simulation Cannot Catch

Even a perfect simulation — one that runs instantly, with zero latency, against a perfectly synchronized state — cannot catch every failure mode.

Simulation runs against the state as seen by the RPC node. The RPC node's state might lag behind the actual chain state. The node might be one or two slots behind the leader. Transactions that have been processed but not yet propagated to the RPC node are invisible to the simulation. The simulation tests against a state that is already slightly stale, even before the bundle-to-execution delta is factored in.

Competition-dependent failures are invisible to simulation. If the bundle fails because another bot's bundle lands first and consumes the opportunity, the simulation cannot predict that. The simulation runs in isolation. It does not know about other bots' bundles. It does not know about pending transactions in the block engine's queue. It evaluates the transaction against the current state, and the current state does not include the effects of transactions that have not yet been committed.

Slot-timing-dependent failures are invisible. If the bundle fails because it arrives during a slot transition, or because the leader rotates between simulation and execution, the simulation cannot model that. Simulation is state-dependent, not time-dependent. It answers "does this work against this state?" not "does this work at this moment in the slot schedule?"

These failure modes are inherently unpredictable. No amount of pre-checking can eliminate them. They are the residual uncertainty that remains after every filterable failure has been filtered out. Pre-simulation is a filter, not a guarantee. It removes the solvable problems and leaves the unsolvable ones.

The Latency Arithmetic

The decision comes down to arithmetic. What does the simulation cost, and what does it save?

The cost is measurable: the time to execute a simulation round trip. This varies by RPC provider, by network conditions, by transaction complexity. It is a concrete number that can be benchmarked, measured, and tracked.

The savings are also measurable, at least in aggregate. How many bundles that fail on-chain would have been caught by pre-simulation? This is the "preventable failure rate" — the percentage of FAILED bundles whose failure was not caused by state changes between simulation and execution, but by problems that existed in the transaction at the time it was built. These are the failures that simulation would have caught.

If the preventable failure rate is high — if many bundles fail for reasons that simulation would detect — pre-simulation saves many wasted submissions. Each wasted submission has a cost: the time spent waiting for the FAILED result, the block engine resources consumed, the opportunity cost of the submission slot. If simulation prevents enough wasted submissions, the time saved by not sending failing bundles exceeds the time spent on simulation.

If the preventable failure rate is low — if most failures are caused by state changes, competition, or timing — pre-simulation catches very few failures. The simulation cost is paid on every transaction, but the savings are rare. The net effect is negative: more time spent simulating than saved by filtering.

The preventable failure rate depends on the quality of the bot's pipeline. A well-optimized bot with accurate profit estimation, fresh data, and robust transaction construction has a low preventable failure rate. Its transactions rarely fail for reasons that simulation would catch — most failures are competition losses or timing issues. For this bot, pre-simulation adds cost with minimal benefit.

A bot with a less mature pipeline — one with occasional calculation errors, stale data, or edge cases in transaction construction — has a higher preventable failure rate. Many of its failures are not competition losses but construction errors. For this bot, pre-simulation is valuable. It catches the mistakes before they reach the block engine.

The Hybrid Approach

The most practical answer is not a universal rule. It is a policy.

Some transactions are worth simulating. Others are not. The bot can make this decision dynamically, based on the characteristics of the specific transaction and the current state of the market.

A transaction targeting a pool that has historically produced many failures deserves simulation. A transaction targeting a pool with a strong track record does not. A transaction with an unusually large trade size deserves simulation — the stakes are higher, and the cost of failure is greater. A transaction with a minimal trade size does not — the cost of failure is low, and the latency cost of simulation is proportionally larger relative to the potential profit.

A transaction built on data that is known to be slightly stale — perhaps the WebSocket connection lagged, or the account data is one slot old — deserves simulation to verify that the staleness has not invalidated the opportunity. A transaction built on data that just arrived — fresh from a real-time data feed, milliseconds old — probably does not need the extra verification.

This is how a pilot uses a preflight checklist. Before a cross-country flight in unfamiliar weather, the checklist is thorough. Every item is checked. Every instrument is verified. Before a five-minute hop around the pattern on a clear day, the checklist is abbreviated. The pilot still checks the critical items — fuel, controls, engine — but skips the weather briefing and the full instrument scan. The level of verification matches the level of risk.

The bot can implement the same logic. High-risk transactions get simulation. Low-risk transactions go straight to submission. The definition of "high risk" and "low risk" is the design decision. It encodes the builder's judgment about where the preventable failure rate is high enough to justify the latency cost of simulation.

The Philosophical Divide

At a deeper level, the pre-simulation question reveals a philosophical divide in how to approach MEV competition.

One philosophy says: quality over quantity. Send fewer bundles, but make every bundle count. Pre-simulate. Filter. Validate. The bundles that reach the block engine are clean, verified, high-confidence submissions. The failure rate is low. The signal-to-noise ratio is high.

The other philosophy says: speed over quality. Send as many bundles as possible, as fast as possible. Some will fail. Many will fail. But the ones that succeed will succeed because they got there first. The failure rate is high, but the success rate — measured in absolute landed bundles, not in percentage terms — might also be high, because speed captured opportunities that a slower, more cautious bot would have missed.

Neither philosophy is objectively correct. They are strategies optimized for different environments. In a low-competition environment — few bots, abundant opportunities, wide profit margins — quality matters more. There is no rush. The opportunities are not disappearing in milliseconds. Taking the time to verify each transaction before submission is a pure improvement with little downside.

In a high-competition environment — many bots, scarce opportunities, razor-thin margins — speed matters more. Every millisecond of delay is a millisecond in which a competitor can submit first. The cost of simulation might exceed the cost of the failures it prevents, because the failures it prevents are cheap (wasted submissions) while the opportunities it costs are expensive (landing a profitable bundle first).

The environment itself changes over time. What was low-competition last month might be high-competition this month. What was high-margin yesterday might be low-margin today. A static policy — always simulate, or never simulate — is always wrong for some portion of the time. A dynamic policy — simulate when it makes sense, skip when it does not — adapts to the changing landscape.

The Check That Changes the Answer

There is one more subtlety to pre-simulation that is easy to overlook. The act of simulating takes time. During that time, the state changes. After the simulation completes, the transaction is either submitted or discarded based on the simulation result. But the state that the bundle will encounter on-chain is not the state the simulation tested against. It is the state that exists after the simulation time has passed.

This means the simulation can change the answer. Not because it modifies anything — it does not — but because the time it consumes allows the world to move. The simulation succeeds, giving you confidence. You submit the bundle. But the fifty milliseconds spent simulating were fifty milliseconds in which the state evolved. The bundle lands against a state that is fifty milliseconds newer than the state the simulation tested. If those fifty milliseconds contained a relevant state change, the simulation's success was misleading.

Without the simulation, the bundle would have been submitted fifty milliseconds earlier and would have encountered a fifty-milliseconds-younger state. Would that state have been more favorable or less favorable? There is no way to know. The simulation step did not just verify the transaction — it shifted the timing of the submission, which shifted the state the submission encounters, which potentially changed the outcome.

This is the observer effect applied to transaction submission. The act of checking alters the thing being checked — not directly, but through the time consumed by the checking process itself. You cannot simulate and submit simultaneously. One must happen before the other. And the one that happens second operates in a different temporal context than the one that happened first.

Where This Leaves Things

Pre-simulation is a tool. Like every tool, it has a cost and a benefit, and the ratio between them depends on the context.

The benefit is clarity. A simulation that catches a failing transaction before it reaches the block engine saves a wasted submission and provides diagnostic information that the block engine never would have returned. The bot knows why the transaction fails. It can log the error. It can adjust the strategy. It can learn from the failure in a way that the opaque FAILED status never allows.

The cost is time. Time that might mean the difference between first and second. Time that shifts the submission timing and changes the state the bundle encounters. Time that is sometimes well spent and sometimes wasted.

The practice test before the exam. You either take it and risk running out of study time, or skip it and risk discovering your mistakes in the testing center. There is no option that eliminates both risks. There is only the choice of which risk to accept.

The question is not whether to simulate. The question is when.

Disclaimer

This article is for informational and educational purposes only and does not constitute financial, investment, legal, or professional advice. Content is produced independently and supported by advertising revenue. While we strive for accuracy, this article may contain unintentional errors or outdated information. Readers should independently verify all facts and data before making decisions. Company names and trademarks are referenced for analysis purposes under fair use principles. Always consult qualified professionals before making financial or legal decisions.