Skip to main content
Every Solana transaction sets (implicitly or explicitly) two parameters: a compute unit limit (max CUs the tx may consume; default 200,000 × number of instructions up to a per-tx cap) and a priority fee in micro-lamports per CU. Under-sizing either kills transactions — too-low CU limits cause ProgramFailedToComplete; too-low priority fees cause the tx to sit unconfirmed until expiry.

The two settings

  • setComputeUnitLimit(units) — caps compute; the transaction pays for at most units CUs.
  • setComputeUnitPrice(microLamports) — priority fee bid, in micro-lamports per CU. Total priority fee = units × microLamports × 1e-6 lamports.
Cost math: a 250k CU limit at 50k micro-lamports/CU bids 250_000 × 50_000 / 1e6 = 12,500 lamports ≈ 0.0000125 SOL ≈ $0.003 at $200 SOL. Priority fees at this scale are noise for most user swaps but material for bots doing 1000 txs/day.

CU benchmarks per instruction

Benchmarks from mainnet execution logs, averaged across recent runs. Numbers are approximate (±15%); re-measure for your specific flows. The “tick crossings” row for CLMM is the biggest CU variable. If you don’t know how many ticks the swap will cross, budget for the worst case — 8 crossings is the hard cap (the program loads at most 8 tick arrays).

Composed transactions

Sum the individual budgets and add:
  • +1,500 CU per CPI frame — the runtime’s fixed overhead for each cross-program call.
  • +20,000 CU per ATA creationcreate_associated_token_account is not free.
  • +5,000 CU for setComputeUnitLimit / setComputeUnitPrice each.
Example: a user swap that creates the output ATA and wraps native SOL:
Padding: set the CU limit ~25% above the expected usage. Under-estimating costs the whole tx; over-estimating just raises priority-fee cost proportionally (priority fee is units × microLamports, so ~25% over-budget costs 25% extra in priority fee).

Priority-fee estimation

Solana’s local fee market means priority fees are per-writable-account. A tx that writes to a hot account (popular pool state) pays more than a tx that writes to a cold account. The global fee level is not the right metric for Raydium swaps; you want fees on the specific pools you’re touching.

Strategy 1: RPC provider estimator

Each major RPC provider publishes a priority-fee estimator that queries recent fees on specific accounts:
Priority levels across most providers: Min / Low / Medium / High / VeryHigh / UnsafeMax. Map them to percentiles: Providers: Helius (getPriorityFeeEstimate), Triton (getRecentPrioritizationFees with account list), QuickNode (similar).

Strategy 2: Direct RPC query

Use the standard getRecentPrioritizationFees RPC:
This is the vanilla Solana RPC method; works with any provider. Downside: the sample is small (150 slots ≈ 60 seconds) and noisy. For smoother estimates, use a provider’s aggregation.

Strategy 3: Historical self-tuning

For bots running constant flow, track your own landed vs. expired rates:
This self-corrects faster than public estimators and captures per-pool structure the public estimators don’t always see.

Handling CU-exhaustion failures

Symptom: tx fails with exceeded maximum number of instructions allowed (200000) or ProgramFailedToComplete. Diagnosis:
Fixes:
  1. Raise the CU limit. If your tx was using 195k of a 200k budget, bump to 300k.
  2. Split the transaction. If you’re hitting the 1.4M per-tx cap, break into two txs. Farm harvest then stake is a classic one to split when rewards are many.
  3. Trim accounts. Each additional writable account adds ~2,000 CU. Pruning unused accounts helps on marginal cases.
  4. Use lookup tables. LUT lookups are ~50 CU per resolved address, saving the 5,000 CU of a full account reference per entry.

Handling stuck transactions

Symptom: tx submitted, never confirms, eventually expires with BlockhashNotFound. Diagnosis:
  • getSignatureStatuses([sig]) returns null → leader never saw it.
  • Returns { confirmationStatus: null } → leader saw it but didn’t include.
Fixes:
  1. Raise priority fee. Re-submit with 2× the current fee.
  2. Rebuild with fresh blockhash. Blockhash lifetime is ~60 seconds; beyond that the tx is invalid regardless of fees.
  3. Multi-RPC broadcast. Some RPCs have better leader connectivity than others. Submit to 3–5 in parallel.
  4. Switch to Jito bundles. See integration-guides/routing-and-mev. Bundles bypass public packet queues.
Retry logic skeleton:

Under congestion

When the network is congested (Jupiter / Jito bundle dashboards show backlog, RPC latency spikes, tx expiry rates climb), adjust: Watching congestion signals:
  • Priority-fee 75th percentile > 500k micro-lamports: congestion.
  • Jito 50th percentile tip > 0.001 SOL: congestion.
  • RPC response p99 > 2s: RPC-specific issue or congestion.

Fee budgeting for bots

A trading bot running ~1000 txs/day needs a priority-fee budget. Back-of-envelope:
That’s the minimum. During congestion, multiply by 5–10×. Plan for ~$150–300/month in priority fees for a steady-flow bot. Bots that must land in specific slots (liquidations, arb) pay 95th percentile continuously and spend ~10× more. Jito bundle tips dominate at that scale — often $1000+/month — but the alternative (being front-run or expiring) is worse.

Pitfalls

1. Forgetting the CU limit

Default is 200k CUs × (instructions in tx). A single-instruction swap defaults to 200k; that’s enough for CPMM on SPL Token but not CLMM with tick crossings or anything Token-2022. Always set it explicitly.

2. Priority fee on the wrong account

If you estimate priority fee against the token mint but the hot account is the pool state, your estimate is too low. The pool state is the right writable-account to target for Raydium.

3. Fees scale with CU limit

total_priority_fee = units × microLamports. Raising units from 200k to 1M at 50k micro-lamports/CU multiplies priority fee 5×. Don’t over-budget CU just in case; measure.

4. Default tx version

Legacy transactions have lower account limits; V0 transactions with address lookup tables unlock larger routes. The SDK uses V0 by default in txVersion: TxVersion.V0. Don’t drop to legacy unless you need wallet compatibility.

5. skipPreflight hides CU errors

skipPreflight: true sends the tx without local simulation. You save ~100ms but lose the early feedback on CU exhaustion. Use it only on retries, not on the first attempt.

Pointers

Sources: