FEE MECHANICS — Solana × Compute Budget

The Solana Fee Math Nobody Explains: Rank = Bid ÷ Cost, Not Bid Alone

6 min read
SolanaCompute BudgetPriority Fees

Everyone knows the recipe for landing a transaction on a busy Solana: raise the priority fee. It works, sometimes, and it costs more every time. What almost nobody knows is the number the block producer actually sorts by, which is not your fee. It is your fee divided by how much of the block you asked for.

That division is why two transactions paying the same lamports can land in opposite halves of the queue, and why the single cheapest improvement to your landing rate is an instruction most wallets never set. Here is the formula from the validator source, every component of the cost, and a worked example with real numbers.


The Formula

When a leader has more transactions than it can fit, its scheduler orders them by a single integer:

priorityreward × 1,000,000 ÷ (cost + 1)

Reward is what the leader earns from your transaction: the whole priority fee plus its share of the base fee. Half of the base fee is burned, so with one signature at 5,000 lamports the leader's share is 2,500.

Cost is the block-space price of your transaction in compute units, which is not the compute you use — it is what the cost model charges the block for carrying you. The + 1 exists only to avoid dividing by zero.

Two transactions with equal priority are ordered by arrival since 31 August 2026. Being two milliseconds earlier now beats one extra lamport.


What "Cost" Is Made Of

The cost model is a disguised microsecond model — 30 compute units per microsecond of expected validator time — and every line item follows from that:

Each signature720 CU
Each secp256k1 precompile signature6,690 CU
Each ed25519 precompile signature2,400 CU
Each secp256r1 (passkey) signature4,800 CU
Each writable account lock300 CU
Instruction data1 CU per 4 bytes
Executionthe compute-unit limit you requested
Loaded accounts data8 CU per 32 KiB page of the limit you requested

The last two lines are where the money is. Execution cost is your SetComputeUnitLimit, or, if you did not set one, 200,000 units per instruction (3,000 for builtins) capped at 1.4 million. Loaded-accounts-data cost is your SetLoadedAccountsDataSizeLimit, or, if you did not set one, the maximum: 64 MiB, which is 2,048 pages, which is 16,384 compute units charged to the block on a transaction that may load 50 KB.

Worth knowing: that 16,384 is block-space cost, not lamports. Your fee is base fee plus price × compute-unit limit; the loaded-data reservation does not appear on the bill. It appears in your rank, which is what decides whether there is a bill at all.


A Worked Example: The Same Lamports, Seventeen Times the Rank

A swap with one signature, eight writable accounts, about 400 bytes of instruction data, a 1,400,000-unit compute limit left at the wallet default, no loaded-data limit, and a priority fee of 200,000 lamports (0.0002 SOL).

Reward200,000 + 2,500 = 202,500 lamports
Cost720 + 8×300 + 100 + 1,400,000 + 16,384 = 1,419,604 CU
Priority202,500 × 1,000,000 ÷ 1,419,605 ≈ 142,600

Now simulate first: the swap actually uses 68,000 units and loads 210 KiB. Set the compute limit to 80,000 and the loaded-data limit to 210 KiB (7 pages, 56 CU). Keep the same 200,000-lamport bid by raising the per-unit price.

Cost720 + 2,400 + 100 + 80,000 + 56 = 83,276 CU
Priority202,500 × 1,000,000 ÷ 83,277 ≈ 2,431,600

Same lamports, 17× the rank. And if instead you keep the same per-unit price and let the fee fall with the limit, the fee drops to about 11,400 lamports and the priority is still ≈ 167,000 — higher than the original, at a seventeenth of the cost. That is the whole trick: in the leader's eyes, asking for less is the same as paying more.


What This Does Not Fix

Rank is position in a queue, not admission. Three things sit outside the formula.

  • The queue has a floor. When the leader's buffer reaches 99% of 100,000 packets, it publishes the priority of the lowest transaction it still holds, and anything below that is discarded before signature verification. A better rank keeps you above the floor; it does not guarantee the floor is low.
  • Accounts conflict. The scheduler cannot run two transactions that write the same account at once. A high-rank transaction on a hot pool waits for the lock regardless of its score, and if the pool has already absorbed its 24-million-unit share of the block, it waits for the next block.
  • Requesting too little compute fails the transaction. A limit below actual usage is an error that burns the fee. Simulate, then add a margin — the reference tooling uses 5% for state-dependent instructions — and set the limit from that.

Transaction v1 Changes the Shape of the Fee

The new v1 transaction format replaces "price × limit" with a flat priority fee in lamports, decoupled from the compute limit, and defaults both the compute limit and the loaded-data limit to zero when unset. Two consequences: an unset v1 transaction is 16,384 units cheaper in the cost model than the identical legacy one, and lowering the compute limit no longer lowers the bid, only the cost. The ranking formula is unchanged; the knobs are cleaner.

Whether it is live where you are sending is a feature gate, and the upgrades tracker reads those accounts from the chain every minute.


Solana Fee Math: Questions People Actually Ask

Does a higher priority fee always land my Solana transaction first?

No. The leader ranks by reward divided by requested cost. A transaction reserving 1.4 million compute units ranks below one reserving 80,000 at the same fee, and equal ranks are ordered by arrival.

What is the 16,384 compute units almost every transaction pays?

The loaded-accounts-data cost when no limit is set: 64 MiB of allowed loaded data at 8 units per 32 KiB page. One SetLoadedAccountsDataSizeLimit instruction sized to your real usage cuts it to a few dozen units.

Does lowering the compute unit limit lower my Solana fee?

On legacy and v0 transactions yes, because the fee is price × limit. On v1 transactions the priority fee is a flat lamport amount, so lowering the limit only lowers your block-space cost and raises your rank.

What happens if I set the compute limit too low?

The transaction fails with a compute exhaustion error and the fee is still charged. Simulate first and add a margin before setting the limit.


The Order of Operations

  • Simulate, and read the units consumed and the bytes loaded.
  • Set SetComputeUnitLimit to usage plus a margin.
  • Set SetLoadedAccountsDataSizeLimit to bytes loaded, rounded up to 32 KiB.
  • Only then set the price, starting from the account floor that getRecentPrioritizationFees gives you.

Most transactions on Solana skip the first three steps and try to solve landing with the fourth. The leader is dividing by a number you never looked at. And when a transaction disappears with no error at all, the cause is usually not the fee — that is a separate set of seven doors.

Constants read from the Agave validator source at commit beee69b958: core/src/transaction_priority.rs, cost-model/src/block_cost_limits.rs and cost-model/src/cost_model.rs. The worked figures are arithmetic on those constants — recompute if any of them changes.

xroot.devOn-chain research, published in full
𝕏 @xrootdev
← Back to all notes