Your swap landed at a different price than the quote because the two numbers are separate readings of the same pool, taken at different moments. The quote is a projection built from reserves at a recent slot. The fill is what the on-chain program computes from the reserves that exist at the instant your transaction executes. Everything below is about what happens in the gap between those two moments, and which parts of it you control.
What happens between a quote and a fill
It helps to see the whole path once, because most confusion comes from imagining a quote as a reservation. It is not. Nothing is held for you, no price is locked, and no counterparty has agreed to anything. A quote is a calculation someone ran on your behalf against state they had recently read. The only part of it that becomes binding is a single number written into the instruction you sign.
- You request a route. The interface asks a quote service for the best way to convert input token A into output token B for a given input amount.
- The service reads pool state. It pulls reserves, fee tiers and tick or bin data for candidate pools as of some recent slot, then simulates the trade across one or several routes.
- It returns two numbers. An expected output, which is a projection, and a minimum output, which is the expected output reduced by your slippage tolerance.
- You sign. Your wallet signs a transaction containing the swap instruction, the route, the minimum output and a recent blockhash.
- The transaction is forwarded. Your RPC provider relays it toward the validator currently producing blocks, and often toward the next ones in the schedule.
- It competes for inclusion. The block producer selects and orders transactions. Yours may be included in the next block, several blocks later, or never.
- The program executes. At execution the swap program reads the pool account as it is right then, computes the output from those reserves, and compares it to your minimum.
- The result settles. Either token balances change and the transaction succeeds, or the instruction reverts and only fees are consumed.
Notice which step contains the price you were shown. Step three. Notice which step contains the price you receive. Step seven. Between them sit signing latency, network relay, block production and the ordering decisions of whoever is producing that block. The expected output never travels on chain in any enforceable form. Only the minimum does, and it functions as a floor rather than a target.
Every point where a quote goes stale
Staleness is not one event. It accumulates in layers, and each layer has a different fix, which is why a single setting rarely solves a persistent fill problem. Work through them in order and you can usually name the layer that is hurting you.
- Read lag. The quote service read the pool at a slot that was already committed. By the time the number renders in your browser it describes the past, not the present.
- Decision time. The seconds between the quote appearing and you pressing the button. On a fast-moving pair this is often the largest single contributor, and it is entirely within your control.
- Signing and relay. Hardware wallet confirmations, extension prompts and the hop to your RPC endpoint all add wall-clock time before the transaction is even in flight.
- Inclusion delay. Once forwarded, the transaction waits for a block producer to pick it up. Under load this wait can span many slots, and the pool keeps trading throughout.
- Retries. Clients commonly rebroadcast the same signed transaction until it lands or expires. Every retry is an attempt to execute an old projection against newer state.
- Ordering inside the block. Even at the moment of inclusion, other transactions in the same block can touch the same pool ahead of yours and change the reserves your instruction reads.
The outer bound on all of this is the blockhash. A transaction references a recent blockhash that remains valid for 150 slots; past that window the transaction can no longer be processed and simply expires. That gives you a hard ceiling of roughly one minute at the target slot time, but treating one minute as your quote's shelf life is a mistake. The economically meaningful shelf life on an active pair is a small number of seconds.
Slot timing and the 400 millisecond target
Solana targets a slot time of about 400 milliseconds. Traders often read this as a guarantee that little can change between quote and fill. That reading confuses the clock with the content. A slot is a time box for producing a block, not a limit on how much activity fits inside it. A single block can contain a large number of transactions, and several of them can touch the pool you are trading against.
So there are two independent clocks to think about. The first is how many slots elapse between the state your quote was built from and the state your transaction executes against. The second is how many pool-touching transactions are ordered ahead of yours inside the block that includes you. The first is a latency problem. The second is an ordering problem, and no amount of speed on your side fully removes it.
Worked example: turning elapsed slots into exposure
Illustrative arithmetic only. Suppose your quote was built from reserves at slot N, and your transaction is included at slot N plus 5. At a 400 millisecond target that is about 2 seconds of pool history you never saw. If the pair averaged one trade per slot in that window, roughly five trades moved the reserves before your instruction read them. Your fill reflects all five.
This is also why a quote that refreshes every few seconds is not an interface annoyance. It is the interface telling you, correctly, that the previous number has expired. If you find yourself signing a quote that has been on screen for twenty or thirty seconds, the mismatch you are about to see is not a bug in the router.
Competing flow in the same pool
On a constant-product automated market maker, price is a pure function of the two reserve balances. Nothing else sets it. That makes the mechanism easy to reason about: anyone who trades before you leaves the pool at a different point on the curve, and you inherit that point.
x × y = kwhere x and y are the two reserve balances and k is the invariant held constant across a swap, before fees are appliedWorked example: one trade ahead of yours
Illustrative arithmetic only, with fees ignored to keep the mechanism visible. Take a pool holding 1,000 units of token X and 100,000 units of token Y, so k is 100,000,000 and the spot price is 100 Y per X. You want to sell 10 X.
Quoted in isolation: the pool would hold 1,010 X, so Y becomes 100,000,000 divided by 1,010, which is about 99,009.90. Your output is about 990.10 Y, an effective price of roughly 99.01 and about 0.99 percent of price impact.
Now suppose one other trader sells 10 X immediately before you, in the same block. The pool your instruction reads holds 1,010 X and about 99,009.90 Y. Your 10 X takes it to 1,020 X, so Y becomes about 98,039.22, and your output is about 970.69 Y. That is roughly 1.96 percent below the quote, produced entirely by one ordinary trade that had nothing to do with you.
Two things are worth extracting from that. First, the shortfall was not caused by the router being wrong; the router was right about the state it saw. Second, the shortfall scales with how much size arrives ahead of you relative to pool depth, which is why the same tolerance behaves completely differently on a deep pair and a thin one. The general relationship between size, depth and cost is covered in our full breakdown of slippage on Solana.
Failure versus a worse fill inside tolerance
These are two different outcomes with two different remedies, and traders routinely diagnose one while suffering the other. The deciding number is the minimum output written into your instruction. If the program can deliver at least that amount, the swap succeeds at whatever price the reserves produce. If it cannot, the instruction reverts and you keep your input tokens.
A revert is not free. The transaction was still signed, transmitted, included and processed, so the base signature fee of 5000 lamports is consumed, along with any priority fee you attached. Since SOL carries nine decimals, that base fee is 0.000005 SOL per signature. It is small individually and not small when you are failing repeatedly on a pair you have not sized correctly.
| Tolerance | Minimum output | Outcome | What you actually gave up |
|---|---|---|---|
| 0.5% | 985.15 | Reverts | Fees only, no fill |
| 1.0% | 980.20 | Reverts | Fees only, no fill |
| 2.0% | 970.30 | Succeeds, barely | About 1.96% versus quote |
| 3.0% | 960.40 | Succeeds | About 1.96% versus quote |
| 5.0% | 940.59 | Succeeds | About 1.96% versus quote, with 3% of unused headroom exposed |
Read the last column carefully. Raising tolerance from 2 percent to 5 percent did not make this particular fill worse. What it did was widen the range of outcomes the transaction would have accepted without complaint. That unused headroom is the difference between what you would have taken and what you must take, and it is exactly the quantity that reordering strategies aim at, which is the subject of how the sandwich pattern works on Solana.
Partial fills: AMM pools versus order books
On a pool swap there is no such thing as a partial fill in the order-book sense. The instruction either moves the full input amount and produces at least the minimum output, or it reverts. There is no mechanism for the program to take half your input, deliver half the tokens, and hand the rest back. Any interface language about partial execution on an AMM route means something else.
What it usually means is a split route. An aggregator can send portions of one order through several pools inside a single transaction. Those legs are still atomic together: if any leg cannot satisfy its constraints, the whole transaction reverts and nothing settles. So a four-leg route does not give you four chances to fill; it gives you four ways to fail, which is one reason longer routes fail more often than short ones.
Limit-order venues behave differently because resting liquidity is discrete. An order can match against part of the book, fill for what is available, and leave the remainder resting or cancel it, depending on the order type. That is a genuine partial fill, with a genuine remainder you can act on. If you are used to that model, the all-or-nothing behaviour of a pool swap reads as a malfunction when it is simply a different settlement rule.
A reverted swap and a partially filled order are not comparable events. One consumed fees and changed nothing; the other changed your position. Do not average them together when you review execution.
Symptom to cause: a diagnostic table
Most execution complaints reduce to a handful of mechanisms. The table below maps the symptom you observe to the cause that most often produces it, the evidence that confirms it, and the change that addresses it. Work from the symptom column and confirm before you change anything, because two of these symptoms have opposite fixes.
| Symptom | Most likely cause | What to check | What to change |
|---|---|---|---|
| Transaction failed with a slippage error | Reserves moved past your minimum output before execution | The minimum output in the instruction against the pool state at the inclusion slot | Reduce order size first; only then widen tolerance, and widen it in small steps |
| Fill worse than the quote but inside tolerance | Ordinary competing flow between the quote read and execution | Elapsed slots between quote and inclusion, and other swaps on the same pool in that window | Shorten the time you hold a quote before signing; refresh before you sign |
| Transaction never landed | It was never included and the blockhash expired after 150 slots | Whether the signature exists on chain at all, and what priority fee was attached | Raise the priority fee or change submission path; do not raise tolerance for this |
| Transaction landed late | Inclusion delay under load, with the pool trading throughout the wait | The gap between the slot you signed against and the slot that included you | Improve the submission path and reduce retry-driven staleness |
| Quote refreshes constantly and never settles | Volatile reserves on a thin or newly active pair | Pool depth relative to your order, and how far each refresh moves the number | Trade a smaller clip, or accept that this pair needs wider tolerance to clear |
| Tiny orders fill fine but large ones fail | Price impact grows faster than size, consuming the tolerance budget by itself | Quoted impact at your size versus at one tenth of your size | Split the order into clips sized against pool depth rather than against your intent |
| Everything fails on one pair only | Structural problem on that pool, not a settings problem | Transfer hooks or fees on the token, route length, and whether depth is concentrated away from spot | Route through a different venue or accept that the pair is not tradeable at your size |
| Output amount lower than expected but the price looks right | A token-level fee or a decimals misreading, not execution at all | Pre and post token balances for every account, and the token program in use | Nothing in your slippage settings; account for the token's own transfer behaviour |
| Failures cluster right after a launch or a news event | Depth and flow both changed at once, so your usual sizing is now oversized | Whether current reserves resemble the ones your habits were calibrated against | Re-derive clip size from current depth instead of reusing yesterday's amount |
Reading your own transaction on an explorer
Guessing is expensive. A signature contains almost everything you need to reconstruct what happened, and reading one takes a couple of minutes once you know the order to read it in. Do this for your next three surprising fills and the pattern will usually be obvious before you finish the third.
- Confirm the status. Establish whether the transaction succeeded or failed, and if it failed, capture the program error rather than the interface message.
- Note the slot. Record the slot that included the transaction and compare it with when you requested the quote. That difference is your staleness budget for this trade.
- Read pre and post token balances. Every token account touched has a before and after amount. The difference for your output mint is your realised fill, independent of any label the interface used.
- Find the minimum output. Locate the enforced floor in the decoded swap instruction and compare it with the realised amount. The distance between them tells you how close you came to reverting.
- Expand the inner instructions. A routed swap shows one inner transfer pair per leg. This is where a split route becomes visible, and where you can see which venue actually produced most of your output.
- Check compute units consumed. Compare consumed units against what the transaction requested, remembering the 1.4 million unit ceiling per transaction. Long routes that sit near their requested limit fail for compute reasons that look nothing like slippage.
- Check the fee paid. Separate the base signature fee from any priority fee. On a failed transaction this is your entire cost, and summing it across attempts is the honest price of a tolerance that is too tight.
- Look at neighbouring activity. Scan the same pool in the same and preceding slot. Ordinary competing flow and deliberate reordering both appear here, and you cannot tell them apart from your own transaction alone.
What to change, in order
The reason to fix these in sequence is that the cheap changes remove most of the problem, and the expensive change makes every remaining problem worse if you apply it first. Widening tolerance is the expensive change: it converts failures into fills, but it also converts a narrow band of acceptable outcomes into a wide one.
- Shorten the time between the quote you see and the signature you produce.
- Size the order against pool depth, not against how much you want to trade today.
- Split large orders into clips and let each clip get its own fresh quote.
- Make sure a transaction that fails to land is diagnosed as a landing problem, not a slippage problem.
- Only after the above, adjust tolerance, and adjust it in the smallest step that clears the pair.
- Verify every change against explorer data rather than against how the next fill felt.
Items one and three are where manual execution loses the most ground, because nobody re-quotes and signs the tenth clip as carefully as the first. Closing that particular gap is the narrow job a Solana trading volume bot does, and the thing to check in any such tool is how old its quote is at the moment of signature, not what it claims about output.
None of this eliminates the gap between quote and fill. The gap is structural: a public pool keeps trading while your transaction is in flight, and no interface can promise you a price it does not control. What you can do is shrink the window, shrink the order, and stop paying for headroom you never needed. The rest of the execution basics section builds out each of those levers in turn.
Frequently asked questions
Why did my swap fill differently from the quote?
Because the quote and the fill read the pool at two different moments. The quote is computed from reserves at a recent slot. The fill is computed by the program from the reserves present when your transaction executes. Any trade that touches the same pool in between moves the price you actually receive.
Does a failed swap still cost a fee?
Yes. The base signature fee of 5000 lamports is charged for processing the transaction, not for the trade succeeding. Any priority fee you attached is also spent. A reverted swap returns your input tokens but not the fees, which is why a very tight tolerance on a busy pair has a running cost.
How long is a Solana quote valid for?
There is no formal validity period. The practical outer bound is the blockhash you signed with, which is valid for 150 slots, roughly one minute at the target slot time. The economic validity is far shorter, because the reserves the quote was built from can change in the very next slot.
Is a worse fill the same as a sandwich attack?
No. Most worse fills are ordinary competing flow: other traders hitting the same pool between your quote and your execution. A sandwich is a deliberate pattern that places trades on both sides of yours. The visible outcome can look identical, so the price alone is not evidence of either one.
Why do large orders fail when small ones succeed?
Price impact grows faster than order size on a constant-product pool, so a large order sits much closer to your minimum-output floor before any competing flow arrives. A small order has room to absorb another trade inside the same tolerance. The same tolerance percentage is a much tighter constraint at size.
What is minimum output and where do I see it?
Minimum output is the floor encoded in the swap instruction: if the program cannot deliver at least that amount, it reverts. Interfaces usually show it as minimum received after your tolerance is applied. It is the only number in the quote that is actually enforced on chain.
Filed under Execution basics by The SolSpread Desk. Worked examples on this page are illustrative arithmetic, not observed market data. Read how we handle numbers in the editorial policy.