Logo
New RPC users get 35% off their first monthView the offer
OnFinality Learn
Performance & Optimization14 min read

Solana Jito Bundles over RPC: Tips, Atomicity, and Failure Diagnosis

A technical deep-dive into Jito bundle mechanics, tip instructions, slot targeting, and a reproducible method for diagnosing why a bundle did or did not land.

TL;DR

A Jito bundle is an ordered array of fully signed Solana transactions submitted to a block engine rather than broadcast to the public cluster, with an atomicity guarantee that all members land consecutively in the target slot or none do. The tip is a plain SOL transfer instruction inside a bundle member, not a protocol-level fee field, and its size determines the bundle's economic attractiveness in the block engine's auction. Because bundles are addressed to a specific slot, the time budget between observing an opportunity and submitting is bounded by the slot time, making latency measurable. Failure diagnosis requires separating structurally invalid submissions (bad signature, stale blockhash, account lock conflict) from valid-but-not-included bundles (tip lost the auction, slot missed) and from already-landed-or-conflicting members. This article provides a runnable Node.js script that assembles a two-transaction bundle with a tip, submits it through the block engine path, and verifies each member's status and slot using getSignatureStatuses.

Bundle Mechanics and Atomicity Guarantees

A Jito bundle is an ordered array of fully signed Solana transactions submitted to a block engine rather than broadcast to the public cluster. The block engine will only include the bundle if every member transaction lands consecutively in the target slot, meaning a single failing member discards the entire submission. This atomicity guarantee is the defining property that separates bundles from standard transaction sends, where each transaction is independent and may land in different slots or not at all.

The block engine receives bundles from searchers and evaluates them for inclusion in the next available slot. Unlike the public cluster's gossip-based transaction propagation, the block engine operates as a privileged path with direct access to block producers. This architecture enables lower-latency inclusion for bundles that win the auction, but it also means bundle inclusion depends on a third-party auction rather than on the reader's RPC endpoint. For standard transaction sends, see Solana RPC timeouts and retry strategy.

Every member transaction in a bundle must be fully signed before submission. The block engine does not sign transactions on behalf of the searcher. This means the searcher must construct, sign, and serialize all transactions locally before sending the bundle to the block engine's endpoint. The bundle is then either accepted for the target slot or rejected; there is no partial inclusion.

  • Bundle = ordered array of fully signed transactions with atomicity across the array.
  • Block engine inclusion requires all members to land consecutively in the target slot.
  • A single failing member discards the entire bundle submission.
  • Submission goes to a block engine, not to the public cluster's gossip network.

Tip Instructions and Economic Attractiveness

The tip is a transfer instruction inside a bundle member, not a fee field. Solana has no protocol-level priority auction that a bundle participates in; the tip is a plain SOL transfer to one of the tip accounts, and the bundle's economic attractiveness is the size of that transfer. This distinction is critical for diagnosis: 'the tip was too low' and 'the bundle was dropped' are different diagnoses. A low tip means the bundle was structurally valid and submitted successfully but lost the auction to a higher-tipping bundle. A dropped bundle means the submission itself failed or was never eligible for the target slot.

The tip-account set is documented by Jito and varies over time. Readers must read the current list from an authoritative source rather than hard-code addresses, because a stale address produces a bundle that is structurally valid and economically invisible. The block engine will not recognize a transfer to an obsolete tip account as a valid tip, so the bundle will not be considered in the auction. Always fetch the current tip accounts from the Jito documentation or API before constructing a bundle.

The tip instruction is typically placed as the last instruction in the last transaction of the bundle, though the exact placement is a convention rather than a protocol requirement. The block engine scans the bundle for a transfer to a recognized tip account and uses that amount to rank the bundle against others targeting the same slot. For more on how transaction fees and compute budgets interact with bundle members, see Solana commitment levels and transaction confirmation.

  • Tip = plain SOL transfer instruction inside a bundle member, not a fee field.
  • Bundle economic attractiveness = size of the tip transfer.
  • Tip accounts are documented and change; read the current list from an authoritative source.
  • A stale tip address produces a structurally valid but economically invisible bundle.

Slot Targeting and Latency Measurement

A bundle is addressed to a specific slot, which makes latency measurable. The time budget between observing an opportunity and submitting the bundle is bounded by the slot time. If the bundle arrives after the target slot's block production window closes, it will not be included regardless of tip size. This means the reader must measure their own submit-to-slot margin rather than assume it. The margin is the difference between the time the bundle is submitted and the time the target slot's block is produced.

To measure this margin, record the timestamp immediately before sending the bundle and the slot number of the target. After the slot passes, query the block engine or the cluster for the bundle's status. If the bundle was not included, compare the submission timestamp against the slot's estimated production time. This measurement is specific to the reader's network path, geographic location, and endpoint configuration. For a broader treatment of latency measurement, see Solana RPC latency: measuring and optimizing.

The slot-targeting rule also means that bundle submission is a race against time. Searchers who consistently land bundles typically optimize their network path to the block engine and their local transaction construction pipeline. The reader should treat submit-to-slot margin as a tunable parameter and measure it under realistic conditions rather than relying on theoretical minimums.

  • Bundles are addressed to a specific slot; late arrival means no inclusion.
  • Submit-to-slot margin is the time between submission and target slot production.
  • Measure your own margin; it depends on network path, location, and endpoint.
  • Optimize transaction construction and network path to reduce margin.

Failure Classes: Invalid, Not-Included, and Conflicting

Failure diagnosis requires keeping three classes rigorously separate. Structurally invalid submissions are surfaced as an error response from the send call. This class includes a bad signature, a stale blockhash, an account lock conflict, or a malformed tip instruction. The block engine rejects the bundle immediately, and no transaction is submitted to the cluster. The error response typically includes a reason code or message that identifies the specific structural problem.

Valid-but-not-included bundles are the second class. Here the send call succeeds, but the bundle does not appear on chain. This happens because the tip lost the auction or the slot was missed. The bundle was structurally valid and economically visible, but another bundle offered a higher tip or arrived earlier. This class requires checking whether the bundle was included in the target slot or any subsequent slot. If it was not included, the tip was likely too low or the submission arrived too late.

Already-landed-or-conflicting bundles are the third class. A member transaction may have executed through another path, such as a standard send or a different bundle. This changes which signatures can still be found on chain. If a member already landed, the bundle's atomicity guarantee is broken, and the remaining members may be rejected or included separately. The reader must check each member's signature status individually to determine what actually happened. For blockhash expiry rules that apply to every member, see Solana durable nonce and blockhash expiry.

  • Structurally invalid: bad signature, stale blockhash, account lock conflict, malformed tip.
  • Valid-but-not-included: send succeeds but bundle does not appear; tip lost auction or slot missed.
  • Already-landed-or-conflicting: a member executed through another path.
  • Check each member's signature status individually to determine actual outcome.

Runnable Node.js Script for Bundle Assembly and Verification

The following Node.js script assembles a two-transaction bundle with a tip instruction, submits it through the block engine path, then verifies what actually happened by fetching each member's status with getSignatureStatuses and checking the confirmed slot. The script prints a table of submitted signature, status, slot, and whether all members landed in the same slot. This script is a template; the reader must replace the placeholder tip account with a current address from the Jito documentation and configure their own keypair and RPC endpoint.

The script uses @solana/web3.js for transaction construction and signing, and axios for HTTP requests to the block engine. The bundle is submitted as a JSON payload containing base64-encoded transactions. The block engine endpoint and authentication method vary by provider; the reader should consult the current Jito bundle documentation for the exact request format. The verification step uses the standard Solana JSON-RPC getSignatureStatuses method, which is documented in the Solana JSON-RPC reference.

const { Connection, Keypair, Transaction, SystemProgram, sendAndConfirmTransaction, PublicKey } = require('@solana/web3.js');
const axios = require('axios');

// Configuration — replace with your own values
const RPC_ENDPOINT = 'https://your-solana-rpc-endpoint';
const BLOCK_ENGINE_URL = 'https://your-block-engine-endpoint/api/v1/bundles';
const TIP_ACCOUNT = new PublicKey('REPLACE_WITH_CURRENT_TIP_ACCOUNT');
const TIP_LAMPORTS = 10000; // 0.00001 SOL — adjust based on current auction

async function main() {
  const connection = new Connection(RPC_ENDPOINT, 'confirmed');
  const payer = Keypair.generate(); // Replace with your funded keypair

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

  // Transaction 1: a simple transfer (replace with your actual transaction)
  const tx1 = new Transaction({ recentBlockhash: blockhash, feePayer: payer.publicKey });
  tx1.add(SystemProgram.transfer({
    fromPubkey: payer.publicKey,
    toPubkey: Keypair.generate().publicKey,
    lamports: 1000,
  }));
  tx1.sign(payer);

  // Transaction 2: tip transfer to Jito tip account
  const tx2 = new Transaction({ recentBlockhash: blockhash, feePayer: payer.publicKey });
  tx2.add(SystemProgram.transfer({
    fromPubkey: payer.publicKey,
    toPubkey: TIP_ACCOUNT,
    lamports: TIP_LAMPORTS,
  }));
  tx2.sign(payer);

  // Serialize transactions to base64
  const serializedTx1 = tx1.serialize().toString('base64');
  const serializedTx2 = tx2.serialize().toString('base64');

  // Submit bundle to block engine
  const bundlePayload = {
    jsonrpc: '2.0',
    id: 1,
    method: 'sendBundle',
    params: [[serializedTx1, serializedTx2]],
  };

  let bundleResult;
  try {
    const response = await axios.post(BLOCK_ENGINE_URL, bundlePayload, {
      headers: { 'Content-Type': 'application/json' },
    });
    bundleResult = response.data;
    console.log('Bundle submission response:', JSON.stringify(bundleResult, null, 2));
  } catch (err) {
    console.error('Bundle submission failed:', err.response ? err.response.data : err.message);
    return;
  }

  // Wait for the target slot to pass
  await new Promise((resolve) => setTimeout(resolve, 2000));

  // Verify each member's status using getSignatureStatuses
  const signatures = [tx1.signature.toString(), tx2.signature.toString()];
  const statusResponse = await connection.getSignatureStatuses(signatures, {
    searchTransactionHistory: true,
  });

  // Print results table
  console.log('\n--- Bundle Verification Results ---');
  console.log('Signature | Status | Slot | Same Slot?');
  const slots = [];
  statusResponse.value.forEach((status, index) => {
    const sig = signatures[index];
    const confirmationStatus = status ? status.confirmationStatus : 'not found';
    const slot = status ? status.slot : 'N/A';
    slots.push(slot);
    console.log(`${sig.slice(0, 8)}... | ${confirmationStatus} | ${slot} |`);
  });
  const allSameSlot = slots.every((s) => s === slots[0] && s !== 'N/A');
  console.log(`All members landed in same slot: ${allSameSlot}`);
}

main().catch(console.error);

Results Table for Endpoint-Specific Measurement

Because bundle inclusion and latency depend on the reader's network path, endpoint, and geographic location, this article does not provide benchmark numbers. Instead, the reader should measure their own submit-to-slot margin and bundle inclusion rate using a results table. Run the script above multiple times under realistic conditions, recording the submission timestamp, target slot, whether the bundle was included, the slot in which it landed, and the tip amount. This table becomes the basis for tuning tip size and submission timing.

A sample results table structure is shown below. Fill it with your own measurements. The goal is to identify patterns: does a higher tip correlate with inclusion? Does a shorter submit-to-slot margin improve inclusion? Are there specific slots or times of day when inclusion is more likely? These patterns are specific to your setup and cannot be generalized from third-party benchmarks.

When measuring, ensure that you are using a consistent RPC endpoint and block engine endpoint. Changes in either can affect results. For endpoint selection and configuration, see Solana RPC endpoints (RPC Assistant).

  • Submission timestamp | Target slot | Included? | Landed slot | Tip (lamports) | Submit-to-slot margin (ms)
  • Run at least 20 trials to establish a baseline.
  • Vary tip size and measure inclusion rate.
  • Vary submission timing and measure submit-to-slot margin.
  • Record endpoint and block engine URL for each trial.

Interaction with Standard Solana Constraints

Every member transaction in a bundle still needs a recent blockhash and will expire on the same schedule as any standard Solana transaction. The bundle layer does not extend blockhash lifetime. If a member's blockhash expires before the bundle is included, the bundle becomes structurally invalid and will be rejected. This means the reader must still manage blockhash freshness, either by submitting promptly or by using durable nonces to extend a member's lifetime.

Every member still consumes a fee and compute budget. The bundle does not bypass Solana's fee market or compute limits. Each transaction must have sufficient compute units and pay the base fee. The tip is an additional transfer on top of these costs. Searchers should account for the total cost per bundle, including base fees, compute unit costs, and the tip, when calculating profitability.

Durable nonces can still be used to extend a member's lifetime, allowing a bundle to be prepared in advance and submitted later without blockhash expiry. This composes with the bundle layer rather than replacing it. The reader should treat bundles as an additional submission path that sits on top of the standard reliability machinery, not as a replacement for it. For a detailed treatment of durable nonces, see Solana durable nonce and blockhash expiry.

  • Every bundle member needs a recent blockhash and expires on the standard schedule.
  • Every member consumes a fee and compute budget; the tip is additional.
  • Durable nonces can extend a member's lifetime and compose with bundles.
  • Bundles add a submission path; they do not replace standard reliability machinery.

Limitations and Tradeoffs

Bundle inclusion depends on a third-party block engine's auction rather than on the reader's endpoint. This means that even a perfectly constructed bundle with a competitive tip may not be included if a higher-tipping bundle targets the same slot. The reader cannot guarantee inclusion through endpoint selection or configuration alone. The block engine's auction dynamics are opaque and may change over time.

Tip profitability is not something this article can promise. The tip is a cost, and the return depends on the value of the opportunity the bundle captures. Searchers must calculate their own profitability based on the specific opportunity and the current auction conditions. A tip that was sufficient yesterday may be insufficient today. There is no fixed tip amount that guarantees inclusion.

The tip account list and API surface change without notice. Jito may add or remove tip accounts, change the block engine endpoint format, or modify the bundle submission API. The reader should verify each behavior against current documentation before relying on it in production. This article describes documented behavior as of the date of writing, but provider-specific behavior varies and may change. For infrastructure that supports standard Solana RPC methods used in verification, see Solana RPC endpoints (RPC Assistant) and API service.

  • Bundle inclusion depends on a third-party auction, not on your endpoint.
  • Tip profitability cannot be promised; calculate your own based on opportunity value.
  • Tip account list and API surface change without notice.
  • Verify each behavior against current documentation before production use.

Troubleshooting Common Bundle Failures

When a bundle fails, the first step is to determine which failure class applies. If the send call returned an error, the bundle was structurally invalid. Check the error message for specific causes: a bad signature means the transaction was not signed correctly; a stale blockhash means the blockhash expired before submission; an account lock conflict means another transaction locked an account in the bundle; a malformed tip instruction means the transfer to the tip account was not constructed correctly. Fix the structural issue and resubmit.

If the send call succeeded but the bundle did not appear on chain, the bundle was valid but not included. This is the most common failure class. Check the tip amount against current auction conditions. If the tip was low, increase it and resubmit. Check the submit-to-slot margin; if the bundle arrived late, optimize your network path or submit earlier. Use the results table to identify whether tip size or timing is the limiting factor.

If a member transaction already landed through another path, the bundle's atomicity guarantee is broken. Check each member's signature status individually using getSignatureStatuses. If one member landed and others did not, the bundle was partially executed, which means the atomicity guarantee was violated. This can happen if a member was submitted separately or if the block engine included only part of the bundle. In this case, the remaining members may need to be resubmitted as a new bundle or as standard transactions. For standard send retry strategies, see Solana RPC timeouts and retry strategy.

  • Send call error → structurally invalid: check signature, blockhash, account locks, tip instruction.
  • Send call success but no inclusion → valid-but-not-included: check tip size and submit-to-slot margin.
  • Member already landed → conflicting: check each signature individually with getSignatureStatuses.
  • Use the results table to distinguish tip-size issues from timing issues.

Next Steps for Production Bundle Sending

To move from experimentation to production, start by establishing a reliable measurement pipeline. Run the script above with consistent configuration and record results in a table. Use the table to tune tip size and submission timing. Once you have a baseline, introduce variability to test robustness: different slots, different times of day, different network conditions. The goal is to understand your inclusion rate and the factors that affect it.

Next, integrate durable nonces if your use case requires preparing bundles in advance. This decouples bundle construction from submission timing and reduces the risk of blockhash expiry. For a detailed guide, see Solana durable nonce and blockhash expiry. Also review Solana commitment levels and transaction confirmation to ensure your verification logic uses the appropriate commitment level.

Finally, consider your infrastructure. Bundle submission benefits from low-latency network paths to the block engine. If you are using a standard RPC endpoint for verification, ensure it supports getSignatureStatuses with searchTransactionHistory. For endpoint options and pricing, see Solana RPC endpoints (RPC Assistant), RPC pricing, and the OnFinality Learn hub. For network-specific details, see Solana network page.

  • Establish a measurement pipeline and record results in a table.
  • Tune tip size and submission timing based on your own inclusion rate.
  • Integrate durable nonces for advance bundle preparation.
  • Verify with getSignatureStatuses using the appropriate commitment level.
  • Optimize network path to the block engine for lower submit-to-slot margin.

Never Worry about Infrastructure Again

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

Get Started