Skip to main content

Strategies to Optimize Solana Transactions

Updated on
Jun 24, 2025

Optimizing transactions on Solana is crucial for ensuring their inclusion in blocks, especially during periods of high network traffic. This document outlines key strategies for improving transaction performance and reliability.

There are several strategies you can take to increase the likelihood of your transaction being included in a block and confirmed by the network quickly. These include:

Use Priority Fees

Solana's priority fees allow you to set an additional fee on top of the base fee for a transaction, which gives your transaction a higher priority in the leader's queue. By bidding more for priority status, your transaction will be more likely to be confirmed quickly by the network.

Priority fees can be added to your transaction by including a setComputeUnitPrice instruction in your transaction:

import { Transaction, ComputeBudgetProgram } from '@solana/web3.js'

const transaction = new Transaction()
// Add your transaction instructions here
const priorityFeeInstruction = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: priorityFeeAmount,
})
transaction.add(priorityFeeInstruction)

Calculating the Right Priority Fee

QuickNode provides a Priority Fee API (Add-on Details | Docs) to fetch recent priority fees for a given Program over a specified number of blocks.

curl https://docs-demo.solana-mainnet.quiknode.pro/ \
-X POST \
-H "Content-Type: application/json" \
-H "x-qn-api-version: 1" \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "qn_estimatePriorityFees",
"params": {
"last_n_blocks": 100,
"account": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
"api_version": 2
}
}'

The method returns an object with a per_compute_unit property that includes estimates for priority fees (in microlamports) based on recent network activity. The response provides fee estimates in four categories: low, medium, high, and extreme. Additionally, the response includes a recommended property that provides a recommended fee level for each compute unit based on the current network conditions.


Priority Fees During High Traffic Periods

During periods of high network congestion, to increase the likelihood of successful transaction processing, we recommend using the recommended property of the response. We monitor the network conditions and adjust the fee level accordingly to maximize the likelihood of successful transaction processing.

Priority Fees Resources

Optimize Compute Units

Optimizing compute units (CU) helps prevent transactions from being dropped due to exceeding block limits. Every transaction on Solana uses CU to process. The more complex the transaction, the more compute units it will consume. The network has a limit on the number of compute units that can be processed in a single block. If your transaction exceeds this limit, it will be dropped. Additionally, priority fees are a function of compute units, so lower compute means savings on fees.

As of October 2024, the following limits/defaults are in place:

LimitsCompute Units
Max Compute per block48 million
Max Compute per account per block12 million
Max Compute per transaction1.4 million
Transaction Default Compute200,000

There are two main ways you can optimize compute units:

  1. On your client-side, you can simulate the transaction to determine the compute units it will consume and set the limit accordingly.
  2. In your Solana programs, you can design your programs to be compute-conscious to limit unnecessary computation when your program is invoked.

Setting Compute Unit Limits

To set a compute unit limit for your transaction, you can use the setComputeUnitLimit instruction. This value is what is evaluated in a block's compute limits.

const testTransaction = new Transaction().add(testInstruction)
const computeUnitInstruction = ComputeBudgetProgram.setComputeUnitLimit({
units: targetComputeUnitsAmount,
})
testTransaction.add(computeUnitInstruction)

Calculating the Right Compute Units

To estimate a transaction's expected compute units, you can use the simulateTransaction method to simulate the transaction and determine the compute units it will consume. To make sure you calculate the correct compute, your simulated transaction must include a setComputeUnitLimit instruction. Use simulateTransaction to determine the compute units a transaction will consume:

const testTransaction = new Transaction().add(testInstruction)
const simulation = await connection.simulateTransaction(testTransaction, {
replaceRecentBlockhash: true,
sigVerify: false,
})
const targetComputeUnitsAmount = simulation.value.unitsConsumed

Efficient Program Design

If you are writing Solana programs, you should be aware that your program code can contribute significantly to the compute units consumed by a transaction. To optimize your programs, you can use the following techniques:

  • Minimize unnecessary logging (if you must log a key, make sure to use .key().log() instead of just .key() because base58 encoding is expensive)
  • User smaller data types (e.g., use u8 instead of u64 if appropriate for your use case)
  • Utilize Zero Copy for large data structures (this will result in lower CU for serializing and deserializing the data)
  • Save bumps into PDAs. By saving your bumps you can reduce the compute required to find_program_address

Compute Units Resources

Use Stake-Weighted Quality of Service (SWQoS)

Stake-Weighted Quality of Service (SWQoS) is a newer mechanism to increase the likelihood of your transaction being included in a block. QuickNode provides a variety of SWQoS options:

  • Using Smart Transactions (or manually building the transaction following best practices) with a priority fee API setting of recommended or higher will use stake from QuickNode's own stake pool, and do the proper assembly of compute and priority fees for the user. Any user sending non-duplicate transactions with recommended priority fees or higher will now be routed through QuickNode's own stake pool.

  • Using Jito bundles, i.e. (Lil' JIT's sendTransaction or sendBundle methods) will use Jito's own stake pool, which has significant stake in the Solana network.

  • For ultra-high priority transactions, including professional traders and high-frequency automated trading, Fastlane, enabled in the QuickNode marketplace, is a SWQoS option using a stake pool from the top 1% largest validators in Solana, and is the absolute best way to increase your transaction landing rate.

Transaction Assembly Best Practices

Combine priority fees and compute unit optimization in your transaction assembly:

  1. Create a transaction with your instructions
  2. Fetch and add priority fees
  3. Add a priority fee instruction to the transaction (with fee level set to the recommended value returned from the Priority Fee API)
  4. Simulate the transaction with a compute unit limit instruction
  5. Add a compute unit limit instruction to the transaction with the computed limit from the simulation
  6. Fetch and add a recent blockhash to the transaction
  7. Sign and send the transaction

Check out our sample code here.

Utilize QuickNode SDK - Smart Transaction

The QuickNode SDK provides methods for handling the above steps for your:

  • sendSmartTransaction: Creates and sends a transaction with optimized settings.
  • prepareSmartTransaction: Prepares an optimized transaction.

Example usage:

const signature = await endpoint.sendSmartTransaction({
transaction,
keyPair: sender,
feeLevel: 'recommended',
})

Check out our docs for more details.

Use Jito Bundles

For advanced transaction optimization and bundling, consider using the Lil' JIT marketplace add-on. This add-on enables the creation of Jito Bundles, allowing for atomic execution of multiple transactions.

The add-on enables you to provide a tip to Jito validator producing the next block to prioritize your transactions (or bundles of transactions). To use the add-on, you must:

  1. Include a SOL transfer instruction in your transaction (or last transaction if you are bundling multiple transactions) to a Jito Tip Account (accesible via getTipAccounts method):
const tipAccounts = await rpc.getTipAccounts().send()
const jitoTipAddress =
tipAccounts[Math.floor(Math.random() * tipAccounts.length)]
  1. Serialize your transaction(s) into base58 strings:
const transactionEncoder = getTransactionEncoder()
const base58Decoder = getBase58Decoder()

const base58EncodedTransactions = signedTransactions.map(transaction => {
const transactionBytes = transactionEncoder.encode(transaction)
return base58Decoder.decode(transactionBytes) as Base58EncodedBytes
})
  1. Send the transaction(s) to the the Jito validator client using sendTransaction or sendBundle methods:
const bundleId = await lilJitRpc.sendBundle(base58EncodedTransactions).send()

Transaction confirmation

To ensure your transaction has landed in a block, you should utilize the getSignatureStatuses method to check the status of your transaction:

const signature = await connection.sendTransaction(transaction, [signer])
const { value: statuses } = await connection.getSignatureStatuses([signature])

Create a simple polling function to check on the status of your transaction. If it is not confirmed after 150 slots (about 60 seconds), your blockhash will expire, and you can retry the transaction.

We ❤️ Feedback!

If you have any feedback or questions about this documentation, let us know. We'd love to hear from you!

Share this doc