sendTransaction to the current leader through a staked stake-weighted quality of service (SwQoS) connection lands far more competitively than broadcasting through a shared public RPC and hoping. Priority fees help, but they only order your transaction within a path; they don’t get it to the leader.
This page is the hub for landing transactions on Solana. Each lever below links to a focused deep-dive with runnable code.
The four levers that land a transaction
| Lever | What it does | Deep-dive |
|---|---|---|
| Well-formed transaction | Sets a compute unit price and an accurate compute unit limit so the network can schedule and price the transaction correctly | Priority fees for faster transactions |
| Fresh blockhash | Keeps the transaction inside its ~80–90 second validity window so it isn’t dropped as expired | Handle the transaction expiry error |
| Submission path | Routes sendTransaction to the current leader through a staked SwQoS connection instead of a shared public RPC — the biggest lever | Solana Trader Nodes with Warp transactions |
| Retry and confirm | Re-broadcasts until confirmation or expiry, then confirms against lastValidBlockHeight | Enhance transfers with retry logic |
Why transactions fail to land
A Solana transaction that never confirms almost always failed at one of four points:- It never reached the leader. Submitted through a congested shared RPC, the transaction competes with everyone else for forwarding to the current leader and can be dropped before it ever arrives.
- Its blockhash expired. Every transaction references a blockhash valid for only 150 blocks (about 60–90 seconds). Miss that window and the network drops it with a
TransactionExpiredBlockheightExceededError. - Its compute unit limit was wrong. An undersized limit makes the transaction fail at execution; an oversized one overpays and can deprioritize it. The limit must match what the transaction actually consumes.
- It was submitted once and abandoned. Without re-broadcasting, a single dropped attempt is the end. RPC nodes rebroadcast generically, but application-controlled retries land more reliably under congestion.
Lever 1: build a well-formed transaction
A landable transaction sets two Compute Budget Program instructions: a compute unit price (the priority fee, in micro-lamports per compute unit) and a compute unit limit sized to what the transaction actually consumes. The priority fee you pay iscompute_unit_price × compute_unit_limit, so an accurate limit both improves landing and avoids overpaying.
Size the limit from a live simulation rather than guessing. This example uses @solana/kit, the current Solana JavaScript SDK — @solana/web3.js v1 still works but is now the maintenance branch:
getRecentPrioritizationFees. Since Agave 2.0, priority fees go entirely to the block-producing validator. Two ways to estimate the price:
- Estimate priority fees with RPC methods —
getRecentPrioritizationFeesfor a percentile-based estimate over your writable accounts. - Analyze adjacent transactions for priority fees — read what actually landed in recent blocks.
Lever 2: use a fresh blockhash and respect expiry
Every Solana transaction references a recent blockhash that is valid for 150 blocks — about 60–90 seconds. The transaction carries alastValidBlockHeight; if the network reaches that height without confirming it, the transaction is dropped and you get a TransactionExpiredBlockheightExceededError.
Fetch the blockhash at the confirmed commitment right before you sign, track its lastValidBlockHeight, and stop retrying once the current block height passes it — then rebuild with a new blockhash. The full diagnosis and handling strategy is in Handle the transaction expiry error. For transactions that must stay valid beyond that window — offline or multi-party signing — use a durable nonce instead of a recent blockhash.
Lever 3: choose the right submission path (the biggest lever)
Where you submitsendTransaction matters more than any fee you attach. There are two paths:
- Shared public RPC. Your transaction reaches an RPC node that forwards it to the current leader over the regular path. Under congestion it competes with every other transaction for that forwarding, and a share of transactions are dropped before the leader ever sees them.
- Staked SwQoS connection, direct to the leader. Transactions submitted through a staked validator connection get stake-weighted priority forwarding to the leader. This is what SwQoS is for, and it’s the difference between “the leader saw my transaction” and “my transaction never arrived.”
sendTransaction call to the current leader through bloXroute’s staked validator connections, while every other request goes through your standard Chainstack Solana node. Warp is available on both Solana Trader Nodes and Global Nodes, works over HTTP (not WebSocket), and needs no custom transaction crafting — you switch the endpoint and your existing setup lands through the staked relay.
Priority fees and the submission path solve different problems. The submission path decides whether your transaction reaches the leader; the priority fee decides its ordering once it’s there. A high priority fee on a transaction that never reaches the leader still doesn’t land.
mainnet.block-engine.jito.wtf), not through a standard RPC node — so this path runs alongside your Chainstack endpoint rather than through it, and complements SwQoS rather than replacing it. See Solana MEV protection for the anti-sandwich dontfront pattern.
For live, continuously-measured landing rates comparing Warp against other providers, see the Chainstack Compare dashboard.
Under heavy, unpredictable load, the most reliable setups fan out — submit the same signed transaction through more than one path at once (for example a staked connection plus a public RPC, or add a Jito bundle). De-duplication by signature makes this safe: the network runs a given transaction at most once, no matter how many paths deliver it.
Lever 4: retry and confirm correctly
A single submission is not enough under congestion. Re-broadcast the same signed transaction until it either confirms or its blockhash expires — never resubmit with a new blockhash while the old one is still valid, or you risk landing the transaction twice.- For the standard path,
sendAndConfirmTransactionFactoryconfirms over websockets against the blockhash andlastValidBlockHeight— see the Lever 1 example. Avoid the legacyconfirmTransactionRPC method; it’s deprecated. - For an aggressive path under congestion, send with
maxRetries: 0andskipPreflight: true, then run your own loop: re-send the same signed transaction every few seconds and pollgetSignatureStatuses, stopping when it confirms or the current block height passeslastValidBlockHeight. Skip preflight only after a successful simulation —skipPreflight: truealso skips the check that catches a malformed transaction, so it hides errors if you haven’t already validated. - Submit through geographically close endpoints to cut propagation latency.
Priority fees vs submission path: which matters more?
Both, in order. First get the transaction to the leader (submission path), then compete for ordering once it’s there (priority fee). A well-tuned priority fee on a shared RPC still loses to a direct-to-leader submission during congestion, because ordering only matters among the transactions the leader actually received. Tune the fee after you’ve fixed the path, not instead of it.FAQ
Why does my transaction getTransactionExpiredBlockheightExceededError?
Its blockhash aged past the 150-block (~60–90 second) validity window before the network confirmed it — usually because it was submitted through a congested path, never reached the leader, and wasn’t rebroadcast in time. Fix the submission path first, then add retry logic. See Handle the transaction expiry error.
Do higher priority fees guarantee my transaction lands?
No. Priority fees order transactions the leader has already received; they don’t get a transaction to the leader. If the transaction is dropped before the leader on a congested shared RPC, no fee saves it. See Lever 3: choose the right submission path.
What is SwQoS and how do I use it?
Stake-weighted quality of service (SwQoS) gives transactions submitted through a staked validator connection priority forwarding to the leader. On Chainstack you get it by enabling Warp transactions on a Trader or Global node — no custom code, just a different endpoint.
How should I size the compute unit limit?
Simulate the transaction, read unitsConsumed, and set the limit to that value plus a small margin (around 10%). A limit that matches actual consumption improves scheduling and avoids overpaying, since the fee is compute_unit_price × compute_unit_limit. See Lever 1: build a well-formed transaction.
Is sendTransaction retrying enough on its own?
The RPC node rebroadcasts generically, but application-controlled retries — re-sending the same signed transaction until it confirms or expires — land more reliably under congestion. See Enhance transfers with retry logic.
Which Solana JavaScript library should I use?
For new code, @solana/kit — the current Solana SDK, shown in Lever 1. @solana/web3.js v1 still works but is the maintenance branch. The deep-dive tutorials linked here currently use v1; the landing concepts are identical.
