What are Smart Transactions?

Smart Transactions is a feature from Helius that abstracts away the complexity of optimizing Solana transactions. At its core, Smart Transactions automatically handle blockhash management, compute unit optimization, priority fee calculation, and transaction confirmation status tracking.

Benefits

  • Simplified Transaction Flow: Focus on your application logic rather than transaction optimization
  • Increased Success Rate: Automatically applies best practices for transaction delivery
  • Cost Optimization: Sets appropriate compute unit limits and priority fees
  • Automatic Polling: Handles transaction confirmation status checking

How It Works

When you use Smart Transactions, Helius:

  1. Fetches the latest blockhash
  2. Builds the initial transaction
  3. Simulates the transaction to calculate optimal compute units
  4. Sets the compute unit limit with an appropriate margin
  5. Sets the recommended priority fee
  6. Sends the transaction via staked connections (when eligible)
  7. Polls the transaction status for confirmation

Available SDKs

Node.js SDK

The sendSmartTransaction method is available in our Helius SDK for Node.js (versions >= 1.3.2). To update to the latest version, run:

npm update helius-sdk

Usage example:

import { Helius } from 'helius-sdk';

const helius = new Helius('your-api-key');

const sendOptions = {
  skipPreflight: true, // Optional
  maxRetries: 0, // Recommended
};

// Instructions is an array of TransactionInstructions
// Signers is an array of Keypairs to sign the transaction
// lookupTables is an optional array of lookup tables
const txid = await helius.rpc.sendSmartTransaction(
  instructions,
  signers, 
  lookupTables, 
  sendOptions
);

Rust SDK

The send_smart_transaction method is available in our Rust SDK (versions >= 0.1.5). To update to the latest version, run:

cargo update helius

Usage example:

use helius::rpc::JsonRpcClient;
use helius_sdk::Helius;

// Initialize the Helius client with your API key
let helius = Helius::new("your-api-key".to_string());

// Create your transaction instructions, signers, etc...

// Send the smart transaction
let signature = helius.rpc.send_smart_transaction(
    instructions,
    signers,
    lookup_tables, // Optional
    send_options,  // Optional
).await?;

Implementation Without SDK

While we recommend using our SDKs for the simplest experience, you can also implement Smart Transactions manually:

// 1. Build initial transaction with your instructions
const initialTransaction = new VersionedTransaction(/* ... */);

// 2. Simulate to get compute units
const testTransaction = /* ... with setComputeUnitLimit to 1.4M */
const simulationResult = await connection.simulateTransaction(testTransaction);
const unitsConsumed = simulationResult.value.unitsConsumed;

// 3. Add margin to compute units
const computeUnitsWithMargin = Math.ceil(unitsConsumed * 1.1);

// 4. Get priority fee recommendation
const priorityFeeResponse = await fetch(/* Priority Fee API request */);
const priorityFee = /* extract from response */;

// 5. Add compute unit limit and priority fee instructions
instructions.push(ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnitsWithMargin }));
instructions.push(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }));

// 6. Build final transaction with all instructions
const finalTransaction = new VersionedTransaction(/* ... */);

// 7. Sign and send
for (const signer of signers) {
  finalTransaction.sign([signer]);
}
const signature = await connection.sendRawTransaction(
  finalTransaction.serialize(),
  { skipPreflight: true, maxRetries: 0 }
);

// 8. Poll for confirmation
// Implement polling logic

Handling Transaction Status

Smart Transactions automatically poll for transaction confirmation. The sendSmartTransaction method has a timeout period of 60 seconds and checks the transaction status every 5 seconds.

If you’re implementing your own polling logic:

async function pollTransactionConfirmation(connection, txSig) {
  const timeout = 15000;  // 15 second timeout
  const interval = 5000;  // 5 second retry interval
  let elapsed = 0;

  return new Promise((resolve, reject) => {
    const intervalId = setInterval(async () => {
      elapsed += interval;

      if (elapsed >= timeout) {
        clearInterval(intervalId);
        reject(new Error(`Transaction ${txSig}'s confirmation timed out`));
      }

      const status = await connection.getSignatureStatuses([txSig]);

      if (status?.value[0]?.confirmationStatus === "confirmed") {
        clearInterval(intervalId);
        resolve(txSig);
      }
    }, interval);
  });
}