Logo
New RPC users get 35% off their first monthView the offer
OnFinality Learn
Network & Protocol Guides12 min read

Solana getFeeForMessage: Estimating Network Fees

Learn how to use Solana's getFeeForMessage RPC method to compute the network base fee for a compiled message and combine it with priority fees for accurate transaction cost estimation.

TL;DR

Solana's getFeeForMessage RPC method returns the network base fee in lamports for a specific compiled message at a given blockhash. This fee includes a fixed per-signature component and a compute-unit component based on the message's requested compute-unit limit. The method returns null when the blockhash is expired or unknown, so estimates must be taken close to submission. The total transaction fee is the sum of this base fee and a client-chosen priority fee (compute-unit limit multiplied by compute-unit price). This guide explains the fee formula, demonstrates getFeeForMessage with Node.js, and provides a reproducible method to measure fees against your own endpoint.

Solana Fee Structure and the Role of getFeeForMessage

Solana transaction fees consist of two parts: a base fee and a priority fee. The base fee is determined by the network and includes a fixed lamports-per-signature component plus a compute-unit component calculated at the cluster's lamports-per-compute-unit rate. The priority fee is optional and chosen by the client, calculated as the compute-unit limit multiplied by the compute-unit price. According to Solana's fee documentation, these components combine to form the total fee a transaction pays.

The getFeeForMessage RPC method returns the base fee in lamports that the network would charge for a specific compiled message at a recent blockhash. It does not include the priority fee, which is a client-side choice. This method is essential for estimating the network-imposed cost before submission, especially when the message's requested compute-unit limit affects the base fee. For a broader overview of Solana RPC methods, see the Solana API guide.

  • Base fee = fixed per-signature fee + (compute-unit limit × lamports per compute unit).
  • Priority fee = compute-unit limit × compute-unit price (set by the client).
  • getFeeForMessage returns only the base fee for a given message and blockhash.
  • The method returns null if the blockhash is not recent or unknown.

How getFeeForMessage Evaluates a Compiled Message

getFeeForMessage accepts two parameters: a base64-encoded compiled message and a commitment level. The compiled message must be in the format expected by the Solana runtime, typically produced by serializing a MessageV0 or legacy Message object. The method evaluates the message against a recent blockhash to determine the base fee. If the blockhash is not found or is not recent, the method returns null, indicating that the fee cannot be estimated for that message at that blockhash.

The returned fee is in lamports and reflects the network's current fee parameters. Because the base fee depends on the message's requested compute-unit limit, any change to setComputeUnitLimit will alter the result. Therefore, you must call getFeeForMessage on the exact message you intend to submit, or at least one with the same compute-unit limit and signature count. For details on blockhash validity, refer to the getLatestBlockhash reference.

  • Parameter 1: base64-encoded compiled message.
  • Parameter 2: commitment level (e.g., 'processed', 'confirmed', 'finalized').
  • Returns: fee in lamports, or null if blockhash is stale/unknown.
  • The message's compute-unit limit directly influences the base fee.

Relationship Between getFeeForMessage and Blockhash Freshness

getFeeForMessage is evaluated against a specific blockhash embedded in the message. If that blockhash is expired or unknown to the cluster, the method returns null. This means fee estimates are only valid for a short window—typically until the blockhash expires, which is around 150 blocks (about 60-90 seconds) on Solana mainnet-beta. To obtain a fresh blockhash, use getLatestBlockhash, which also returns the lastValidBlockHeight for transaction submission timing.

Because of this dependency, you should call getFeeForMessage immediately before signing and sending the transaction. If you delay, the blockhash may expire, and the fee estimate becomes invalid. For strategies to handle blockhash expiry, including durable nonces, see Solana blockhash expiry and durable nonces.

  • getFeeForMessage returns null if the blockhash is not recent.
  • Blockhash validity is typically ~150 blocks (~60-90 seconds).
  • Always fetch a fresh blockhash and estimate fees just before submission.
  • Use getLatestBlockhash to get both blockhash and lastValidBlockHeight.

Composing Total Transaction Fees: Base Fee Plus Priority Fee

The total fee a transaction pays is the sum of the base fee returned by getFeeForMessage and the priority fee chosen by the client. The priority fee is calculated as the compute-unit limit multiplied by the compute-unit price (in micro-lamports per compute unit). This priority fee is not included in getFeeForMessage; it must be added separately. The formula is: total fee = getFeeForMessage(message, commitment) + (computeUnitLimit × computeUnitPrice).

The priority fee is a market-driven value that you set to incentivize timely inclusion. It is not a network-determined value. For guidance on estimating priority fees using getRecentPrioritizationFees, see Solana priority fee estimation and setComputeUnitPrice and Solana getRecentPrioritizationFees local fee estimator.

  • Total fee = base fee (from getFeeForMessage) + priority fee (client-chosen).
  • Priority fee = compute-unit limit × compute-unit price.
  • getFeeForMessage never includes the priority fee.
  • Priority fees are optional but can improve transaction landing times.

Distinguishing getFeeForMessage from getRecentPrioritizationFees

getFeeForMessage and getRecentPrioritizationFees serve different purposes. getFeeForMessage returns the network base fee for a specific message, including the compute-unit component based on the message's requested limit. getRecentPrioritizationFees, by contrast, returns a sample of priority fees that other transactions recently paid, expressed in micro-lamports per compute unit. It does not include the base fee and is not specific to your message.

This distinction is crucial: getFeeForMessage tells you what the network will charge, while getRecentPrioritizationFees helps you choose a competitive priority fee. For account-based chains like Ethereum, fee estimation often uses eth_feeHistory, but Solana's model separates base and priority fees. Understanding both methods allows you to compute the exact total cost. For more on commitment levels, see Solana commitment levels and transaction confirmation.

  • getFeeForMessage: base fee for a specific message (network-determined).
  • getRecentPrioritizationFees: sample of recent priority fees (market data).
  • Base fee includes compute-unit component; priority fee is separate.
  • Use both to estimate total cost accurately.

Runnable Node.js Example: Estimating Base Fee and Total Fee

The following Node.js script uses @solana/web3.js to build a message with an explicit compute-unit limit, call getFeeForMessage, and then add a chosen priority fee to compute the total. It also demonstrates the null result when using a stale blockhash. Ensure you have a Solana RPC endpoint; you can use a public endpoint or a dedicated one from OnFinality's Solana network.

The script first fetches a recent blockhash, compiles a MessageV0 with a compute-unit limit of 200,000, and calls getFeeForMessage. It then calculates the priority fee using a compute-unit price of 1,000 micro-lamports (1 micro-lamport = 0.000001 lamports). Finally, it simulates a stale blockhash by using an old blockhash to show the null return.

const { Connection, MessageV0, PublicKey, SystemProgram, TransactionMessage, ComputeBudgetProgram } = require('@solana/web3.js');

async function estimateFees() {
  const connection = new Connection('https://your-solana-rpc-endpoint', 'confirmed');
  const payer = new PublicKey('YourPayerPublicKey');
  const recipient = new PublicKey('RecipientPublicKey');

  // Fetch a recent blockhash
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');

  // Build a message with an explicit compute-unit limit
  const computeUnitLimit = 200000;
  const computeUnitPrice = 1000; // micro-lamports per compute unit
  const instructions = [
    ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnitLimit }),
    ComputeBudgetProgram.setComputeUnitPrice({ microLamports: computeUnitPrice }),
    SystemProgram.transfer({ fromPubkey: payer, toPubkey: recipient, lamports: 1000000 })
  ];

  const messageV0 = new TransactionMessage({
    payerKey: payer,
    recentBlockhash: blockhash,
    instructions,
  }).compileToV0Message();

  // Serialize and encode to base64
  const serializedMessage = messageV0.serialize();
  const base64Message = Buffer.from(serializedMessage).toString('base64');

  // Call getFeeForMessage
  const feeResponse = await connection.getFeeForMessage(messageV0, 'confirmed');
  const baseFee = feeResponse.value;
  console.log('Base fee (lamports):', baseFee);

  // Calculate priority fee
  const priorityFee = computeUnitLimit * computeUnitPrice / 1e6; // convert micro-lamports to lamports
  console.log('Priority fee (lamports):', priorityFee);

  // Total fee
  const totalFee = baseFee + priorityFee;
  console.log('Total fee (lamports):', totalFee);

  // Demonstrate null with stale blockhash
  const staleBlockhash = '11111111111111111111111111111111'; // invalid blockhash
  const staleMessage = new TransactionMessage({
    payerKey: payer,
    recentBlockhash: staleBlockhash,
    instructions,
  }).compileToV0Message();
  const staleFeeResponse = await connection.getFeeForMessage(staleMessage, 'confirmed');
  console.log('Stale blockhash fee (should be null):', staleFeeResponse.value);
}

estimateFees().catch(console.error);

Reproducible Measurement Method and Results Table

To verify fee behavior on your own endpoint, run the script above with different message configurations. Vary the number of signatures (by adding signers) and the compute-unit limit, then record the base fee returned by getFeeForMessage. Also record your chosen priority fee and the computed total. This method is reproducible and helps you understand how each parameter affects the fee.

Use the following table to log your measurements. Fill it in with data from your own runs. Note that the base fee may vary slightly depending on cluster parameters, and the priority fee is entirely your choice. For a production-ready RPC endpoint, consider OnFinality's API service or review RPC pricing for options.

  • Signature count: number of required signatures in the message.
  • Compute-unit limit: the limit set via ComputeBudgetProgram.setComputeUnitLimit.
  • getFeeForMessage result: base fee in lamports (or null if stale).
  • Chosen priority fee: compute-unit limit × compute-unit price (in lamports).
  • Total fee: base fee + priority fee.
| Signature Count | Compute-Unit Limit | getFeeForMessage (lamports) | Chosen Priority Fee (lamports) | Total Fee (lamports) |
|-----------------|--------------------|-----------------------------|--------------------------------|----------------------|
| 1               | 200,000            |                             |                                |                      |
| 2               | 200,000            |                             |                                |                      |
| 1               | 400,000            |                             |                                |                      |
| 1               | 200,000 (stale)    | null                        |                                |                      |

Limitations and Tradeoffs in Fee Estimation

The exact fee is a cluster parameter that can change over time. The lamports-per-compute-unit rate and the fixed per-signature fee are set by the cluster and may be adjusted via governance. Therefore, getFeeForMessage returns the fee based on current parameters, but these can shift. Always treat the returned value as an estimate, not a guarantee.

getFeeForMessage returns null near blockhash expiry, which means you cannot rely on a cached estimate. You must fetch a fresh blockhash and re-estimate if the previous one expires. Additionally, the priority fee is a client choice, not a network value; it is not included in getFeeForMessage and must be added manually. This separation gives you control but requires careful composition. For more on commitment levels and their impact on data freshness, see Solana commitment levels and transaction confirmation.

  • Cluster fee parameters can change; estimates are not guaranteed.
  • getFeeForMessage returns null for stale or unknown blockhashes.
  • Priority fee is client-chosen and not included in the base fee.
  • Always re-estimate fees immediately before submission.

Troubleshooting Common getFeeForMessage Issues

If getFeeForMessage returns null, the most likely cause is an expired or unknown blockhash. Ensure you are using a recent blockhash from getLatestBlockhash and that you call getFeeForMessage promptly. If the problem persists, verify that the message is correctly compiled and serialized; an invalid message format may also cause errors. Check that your RPC endpoint is synced and responsive.

Another common issue is a mismatch between the message used for estimation and the one actually submitted. If you change the compute-unit limit or add instructions after estimating, the base fee may differ. Always estimate on the final message. If you receive an error instead of a null, inspect the error message for clues—it may indicate an invalid parameter or a network issue. For endpoint reliability, consider using a dedicated provider like OnFinality's Solana network.

  • Null result: stale blockhash—fetch a new one and retry.
  • Error: check message serialization and RPC endpoint health.
  • Fee mismatch: ensure the estimated message matches the submitted one.
  • Use a reliable RPC endpoint to avoid timeouts or sync issues.

Next Steps: Integrating Fee Estimation into Your Workflow

To build a robust fee estimation workflow, combine getFeeForMessage with getRecentPrioritizationFees to choose a competitive priority fee. Fetch a fresh blockhash, compile your message with the desired compute-unit limit, call getFeeForMessage, then add your chosen priority fee. Monitor the total fee and adjust the priority fee based on network conditions. For a deeper dive into priority fee strategies, see Solana priority fee estimation and setComputeUnitPrice.

Explore more Solana RPC guides and tutorials on the OnFinality Learn hub. If you need a high-performance RPC endpoint for production, review RPC pricing or get started with OnFinality's API service. For a comprehensive list of Solana RPC methods, refer to the Solana API guide.

  • Combine getFeeForMessage with getRecentPrioritizationFees for total cost.
  • Always use a fresh blockhash and estimate just before submission.
  • Adjust priority fees dynamically based on network congestion.
  • Leverage reliable RPC infrastructure for consistent fee estimation.

Never Worry about Infrastructure Again

OnFinality takes away the heavy lifting of DevOps so you can build smarter and faster.

Get Started