Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.raydium.io/llms.txt

Use this file to discover all available pages before exploring further.

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

import { ComputeBudgetProgram } from "@solana/web3.js";

const tx = new Transaction()
  .add(ComputeBudgetProgram.setComputeUnitLimit({ units: 250_000 }))
  .add(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 50_000 }))
  .add(yourRaydiumSwapIx);
  • 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.
InstructionSPL TokenToken-2022 (simple)Token-2022 (transfer fee)
CPMM initialize_pool180,000200,000
CPMM swap_base_input140,000180,000200,000
CPMM swap_base_output150,000185,000205,000
CPMM deposit130,000160,000180,000
CPMM withdraw120,000150,000170,000
CLMM create_pool70,00085,000
CLMM open_position_v2120,000140,000160,000
CLMM increase_liquidity_v2150,000175,000195,000
CLMM decrease_liquidity_v2140,000165,000185,000
CLMM swap_v2 (0 tick crossings)170,000205,000225,000
CLMM swap_v2 (1 tick crossing)220,000255,000275,000
CLMM swap_v2 (3 tick crossings)320,000355,000375,000
CLMM collect_fee80,00095,000105,000
AMM v4 swap_base_in140,000
AMM v4 deposit120,000
AMM v4 withdraw110,000
Farm v6 create_farm70,00085,000
Farm v6 deposit (1 reward slot)130,000155,000175,000
Farm v6 deposit (3 reward slots)220,000255,000275,000
Farm v6 withdrawmatches deposit
Farm v6 harvestmatches deposit
Farm v3/v5 deposit100,000
LaunchLab initialize100,000
LaunchLab buy_exact_in140,000
LaunchLab graduate250,000
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:
wrap_sol (create_ata + system transfer + sync_native)   ≈ 30,000
CPMM swap_base_input (SPL)                              ≈ 140,000
close_account (unwrap)                                  ≈ 5,000
ComputeBudget instructions                              ≈ 10,000
────────────────────────────────────────────────────────
Total                                                   ≈ 185,000 → budget 250,000
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:
// Helius
const response = await fetch(`https://mainnet.helius-rpc.com/?api-key=${apiKey}`, {
  method: "POST",
  body: JSON.stringify({
    jsonrpc: "2.0",
    id:      "fee-estimate",
    method:  "getPriorityFeeEstimate",
    params: [{
      accountKeys: [poolStatePubkey.toBase58()],
      options:     { priorityLevel: "High" },
    }],
  }),
});
const { result } = await response.json();
const microLamports = result.priorityFeeEstimate;
Priority levels across most providers: Min / Low / Medium / High / VeryHigh / UnsafeMax. Map them to percentiles:
LevelPercentileUse case
Min25thBackground, non-urgent bot traffic
Low50thNormal user swaps
Medium60thDefault for wallet UIs
High75thTime-sensitive arbitrage
VeryHigh95thLiquidations, last-chance exits
Providers: Helius (getPriorityFeeEstimate), Triton (getRecentPrioritizationFees with account list), QuickNode (similar).

Strategy 2: Direct RPC query

Use the standard getRecentPrioritizationFees RPC:
const fees = await connection.getRecentPrioritizationFees({
  lockedWritableAccounts: [poolStatePubkey],
});

// fees: Array<{ slot, prioritizationFee }>
// Recent N slots; default ~150 slots.

const median = percentile(fees.map(f => f.prioritizationFee), 0.5);
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:
per-pool target: 80% land rate at <30s
if current_land_rate < 80%: priorityFee += 10%
if current_land_rate > 95%: priorityFee -= 5%
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:
solana confirm <tx-sig> -v
# Look for "consumed N of M compute units" and which instruction exhausted.
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:
async function submitWithRetry(buildTx, maxAttempts = 5) {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const tx = await buildTx({
      priorityFee: basePriorityFee * Math.pow(1.5, attempt),
      blockhash:   (await connection.getLatestBlockhash()).blockhash,
    });

    try {
      const sig = await connection.sendRawTransaction(tx.serialize(), {
        skipPreflight: attempt > 0,  // skip after first try to save latency
      });

      const result = await connection.confirmTransaction(sig, "confirmed");
      if (result.value.err) {
        // Logic error; don't retry.
        throw result.value.err;
      }
      return sig;

    } catch (e) {
      if (isExpiredError(e)) continue;  // retry
      if (isRevertError(e)) throw e;    // don't retry; deterministic failure
      throw e;
    }
  }
  throw new Error("submit: exhausted retries");
}

Under congestion

When the network is congested (Jupiter / Jito bundle dashboards show backlog, RPC latency spikes, tx expiry rates climb), adjust:
ParameterNormal conditionsCongested conditions
CU limit+25% above estimate+25% above estimate (unchanged)
Priority fee percentile50th75th–95th
Retry count35–7
Retry backoff500ms1000ms
Use Jito bundlesOptionalStrongly recommended
Blockhash refresh on retryYesYes, mandatory
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:
Average CU per tx:          ~250,000
50th percentile fee:        ~20,000 micro-lamports/CU
Cost per tx:                250_000 × 20_000 × 1e-6 = 5_000 lamports = 5e-6 SOL
Daily cost (1000 tx):       5e-3 SOL ≈ $1 @ $200 SOL
Monthly cost:               ~$30
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: