Strike Log

LAT-01

Priority Fees in Practice: What You Are Actually Buying

A priority fee is not a tip to the network and not a speed setting. It is a price per compute unit, multiplied by the units you asked for rather than the units you used, and it changes exactly one thing: your position when a leader has more candidates than space.

Fault card

Symptom
Fees rise, landing does not, and cost per swap doubles
Mechanism
Fee scales with the requested unit limit, not with consumption
Instrument
getRecentPrioritizationFees, simulated unit consumption, fee per landed swap
Correction
Tighten the limit first, then price the units

A Solana priority fee is the requested compute unit limit multiplied by a price in micro-lamports per unit, divided by one million to give lamports. It is paid on top of the base fee of 5,000 lamports per signature, it is charged whether the transaction succeeded or failed, and it influences ordering at the leader rather than delivery to it.

The formula, exactly

Two compute budget instructions matter. One sets the unit limit the transaction is allowed to consume, and the other sets the price you are willing to pay per unit. The priority fee is their product, expressed in micro-lamports and converted down: a micro-lamport is one millionth of a lamport, and a lamport is one billionth of a SOL.

Written out, the priority fee in lamports equals the requested limit times the price in micro-lamports per unit, divided by 1,000,000. The base fee sits separately at 5,000 lamports for each required signature and does not scale with compute. Total cost for a landed transaction is the sum of the two.

Both instructions are optional. Omitting the price means a priority fee of zero, which is a legitimate setting during quiet periods and a poor one during contention. Omitting the limit means the runtime applies a default budget per instruction, which is almost always more than a simple swap needs and therefore more expensive than necessary once a price is attached.

You pay for what you asked for

This is the part that costs operators real money. The fee is computed from the limit requested, not from the units consumed. A transaction that asks for the per-transaction ceiling and then uses a fraction of it pays for the whole request at whatever price it set.

The consequence is that limit and price are not independent dials. Halving the requested limit halves the priority fee at an unchanged price, which means the cheapest way to become more competitive per unit is often to ask for less. An operator who doubles the price without touching an inflated limit has quadrupled the bill relative to what a tightened build would have paid for the same aggressiveness.

There is a floor on how far this goes. A limit set below actual consumption means the transaction lands, exhausts its budget and reverts, paying the fee for nothing. The correct procedure is to simulate, read the consumed units, and set the limit slightly above that figure with a margin for state that may have shifted. The margin is the price of safety and should be chosen deliberately rather than left at a round number.

What the fee actually buys

When a leader has more valid candidate transactions than it can fit, it has to choose. The priority fee is the input to that choice, and it is expressed per compute unit so that a transaction asking for a lot of block resource has to pay proportionally for the privilege. Paying more moves you up the ordering among transactions competing for the same resource.

The competition is narrower than it first appears. What matters is not the network-wide fee level but the fee level among transactions that need the same write locks you do. A transaction writing to a quiet account competes with almost nothing; a transaction writing to a pool that is being traded heavily competes with everything else aimed at that pool. This is why a single global fee setting performs badly across a mixed workload.

What it cannot fix

Naming the limits is what stops fee escalation from becoming a reflex. Each item below is a failure that a higher fee cannot touch, and each is answered somewhere else in this log.

ProblemWhy the fee is irrelevantWhere the fix lives
Blockhash expiredThe message is no longer acceptable at any priceFetch ordering and deadline handling
Never delivered to a leaderNo leader ever saw the feeResend cadence and endpoint choice
Instruction returned an errorThe transaction landed; the fee did its job and was chargedTolerances and account state
Compute budget exhaustedThe limit was too low; price is a separate dialSimulated unit consumption
Endpoint rate limitThe request was refused before the network was involvedRequest budgeting
Account write-lock ceiling reachedA block-level resource limit, not an ordering questionSpreading writes or accepting the wait

Only one row in that table is a fee problem, and it is the compute limit rather than the price. This is the practical reason to work through the landing checkpoints before touching fees: five of six common failures are unaffected by the setting most operators reach for first.

Observing the market you are bidding into

The recent prioritization fees method returns the fees paid in recent blocks and accepts a list of accounts, which scopes the answer to transactions writing the same accounts as yours. That scoping is what turns a vague market number into a usable one. The published method reference describes the shape of the response.

Used well, it becomes a policy input rather than a dashboard. Sample it for the accounts a given intent will write, take a percentile rather than a maximum, and set the price from that. Sampling the maximum is how a fee policy ends up bidding against a single outlier transaction that had reasons of its own.

Every fee policy is a bet about the next few hundred milliseconds made with information about the last few blocks. That gap is irreducible. The reasonable response is to keep the policy simple, log what it paid, and judge it on cost per landed swap rather than on whether any individual bid looked clever.

Three fee policies compared

Nearly every sender ends up implementing one of three policies, whether or not it is named as such. They differ in how much information they consume and in how they fail, and the failure mode is the more useful thing to compare because all three look fine when the network is quiet.

PolicyInputFails bySuits
Fixed priceA number in a config fileOverpaying when quiet, invisible when busyLow-stakes runs and first implementations
Percentile of recent feesRecent fees for the accounts writtenChasing a spike that has already passedMost automated trading
Outcome feedbackIts own landing rate over a windowOscillating, and confusing delivery faults for pricing faultsLong runs with disciplined measurement

The fixed price is worth defending as a starting point. It costs nothing to implement, its behaviour is completely predictable, and its overpayment during quiet periods is small in absolute terms. Its real weakness is that it gives no signal: when landings fall, the operator has no idea whether the price was near the market or nowhere near it.

The percentile policy is the reasonable default because its input is the right input. It bids relative to the transactions that are actually competing with yours rather than to a network average. Its characteristic mistake is reacting to the tail of a spike, so a smoothing window and a ceiling are both worth having, and the ceiling should be a number the operator chose while calm.

Outcome feedback is attractive and easy to get wrong. If the policy raises the price whenever landings fall, it will raise the price during an expiry problem, a delivery problem and an endpoint outage, none of which it can fix. Feedback only works when the input is a landing failure that has already been classified as a scheduling failure, and that classification is exactly what the landing section of this log exists to produce.

  • Write down a maximum price the policy may never exceed, and put it in configuration rather than in code.
  • Scope any market sample to the accounts the transaction will write, not to the network as a whole.
  • Log the price paid on every attempt, or no policy can ever be evaluated after the fact.
  • Keep the limit-tightening step separate from the price step, so the two effects can be told apart.
  • Recheck the policy against a quiet window as well as a busy one, since overpayment hides in calm markets.

Worked fee arithmetic

The table below is illustrative arithmetic using the documented formula and the documented base fee. The prices are chosen to show the shape of the relationship and are not observations of any market.

Requested limitUnit pricePriority feeBase feeTotal per landed transaction
1,400,0001,000 micro-lamports1,400 lamports5,0006,400 lamports
200,0001,000 micro-lamports200 lamports5,0005,200 lamports
200,00010,000 micro-lamports2,000 lamports5,0007,000 lamports
120,00010,000 micro-lamports1,200 lamports5,0006,200 lamports
120,00050,000 micro-lamports6,000 lamports5,00011,000 lamports

Compare rows three and four. Tightening the requested limit from 200,000 to 120,000 units cut the priority fee by 40 percent while leaving the price per unit identical, so the transaction is exactly as competitive per unit as it was and costs less. That is the free move, and it is available to anyone willing to run a simulation before setting a limit.

Now compare rows four and five. Raising the price fivefold raised the priority fee fivefold, which is the honest cost of becoming more aggressive. At 11,000 lamports per landed transaction, a run of 1,000 landed swaps costs 11,000,000 lamports, or 0.011 SOL in fees, and that total includes the ones that landed and reverted.

An escalation ladder with prices

Escalation should be a ladder with stated costs rather than an instinct. Each rung below names what is being bought and what it charges.

  1. Tighten the requested limit from simulation. Buys a lower fee at the same aggressiveness. Costs one simulation call per build and a rebuild when routes change.
  2. Set a price from a percentile of recent fees on your accounts. Buys relevance. Costs a sampling call and the complexity of a percentile policy.
  3. Raise the percentile for the intents that matter. Buys ordering on the subset you care about. Costs proportionally more lamports on those intents only.
  4. Raise the price for all intents. Buys a broad ordering improvement. Costs the increase multiplied by every landed transaction, including reverted ones.
  5. Reduce concurrency instead. Buys less self-competition on the same accounts. Costs throughput, which is often the cheaper currency.
  6. Accept the loss for this window. Buys nothing and costs nothing, and during a genuine block-limit event it is the correct answer.

The fifth rung is the one that gets skipped. When several of your own transactions want the same write lock, some of the competition being priced against you is you. Lowering concurrency can raise the landing rate at zero fee cost, and it is worth testing before the fourth rung rather than after it.

Tips, bundles and a different mechanism

Tips paid to block engine operators are a separate submission path, not a stronger priority fee. A bundle is a set of transactions submitted together with an atomicity guarantee, and the tip is a transfer inside it that pays for that treatment. The mechanism only applies when the transaction is submitted through that path.

The practical implication is that mixing the two concepts wastes money in both directions. Adding a tip transfer to an ordinary RPC send pays an unrelated account for nothing. Sending through a bundle path with no tip and expecting priority-fee behaviour misunderstands what the path is for. Choose the mechanism deliberately, and price it on its own terms.

For most straightforward automated trading, the ordinary path with a well-sized limit and an observed price is sufficient, and the bundle path exists for cases where atomicity across several transactions is the requirement. Anyone comparing a professional Solana volume bot against a self-built sender should ask which path it uses and why, because the answer determines what the fee line in its reporting even means.

Fee as a line item

The number that makes fee policy arguable is cost per landed swap, computed as total lamports spent divided by transactions that landed and succeeded. It absorbs the reverted transactions into the denominator's absence, which is correct, because a reverted swap is a cost with no position to show for it.

Tracked over a run, that single figure catches most fee mistakes. A policy that raised the price and did not raise landings shows up immediately as a higher cost per swap with no compensating change. A limit that was tightened correctly shows up as a lower cost per swap with landings unchanged, which is the shape of a real improvement.

It is also the figure to demand from any tool that quotes a rate. A percentage detached from what it cost to achieve is not a performance claim, and comparing two engines on landing rate alone rewards whichever one bid more of your money. When reading how a vendor describes Solana volume bot cost, the question is whether network fees are separated from service fees, because those two lines behave completely differently as a run scales.

Questions this entry keeps getting

How is the Solana priority fee calculated?

It is the requested compute unit limit multiplied by the compute unit price in micro-lamports, divided by one million to convert to lamports. A limit of 200,000 units at a price of 10,000 micro-lamports per unit produces a priority fee of 2,000 lamports, charged in addition to the base fee of 5,000 lamports per signature.

Does the priority fee depend on the units my transaction actually consumes?

No, and this is the most expensive misunderstanding in fee tuning. The fee is computed from the limit you requested. A transaction that requests 1,400,000 units and consumes 90,000 pays the priority fee for 1,400,000. Tightening the requested limit reduces the bill without changing the price you are willing to pay per unit.

Do I pay the priority fee if my transaction fails?

Yes, if it landed. Fees are charged on inclusion, not on success. A transaction that was packed into a block and then reverted on a slippage check pays its base fee and its full priority fee. Only transactions that never reached a block escape the charge entirely.

How do I know what price to set?

Observe rather than guess. The recent prioritization fees method returns fees paid in recent blocks, and it can be scoped to the accounts your transaction writes to, which is the competition that actually matters. Setting a price without looking at that data is bidding blind into an auction you cannot see.

Is a Jito tip the same thing as a priority fee?

No. A priority fee is a compute budget instruction interpreted by the leader when it orders candidate transactions. A tip is a transfer to a designated account inside a bundle submitted through a block engine, which is a different submission path with different guarantees. Paying a tip on an ordinary RPC send buys nothing.

Will raising the fee fix an expired transaction?

No. Expiry is a deadline written into the message and a leader that never saw the transaction cannot be persuaded by its fee. Raising the fee helps only where the transaction reached a leader that had to choose between candidates. Applying it to an expiry problem spends money and changes nothing.

Filed under Latency. Arithmetic on this page is either a documented protocol constant or an illustrative example built from numbers you supply. If a figure here is wrong or has moved, send the desk a correction and the entry gets amended rather than quietly rewritten.