Skip to content

ade1256/solana-transaction-optimizer

Repository files navigation

Solana Transaction Optimizer

Optimize Solana transactions by chunking large transactions into smaller batches. Supports multiple signers, complex instructions, and adaptive sizing.

Installation

npm install solana-transaction-optimizer
# or
yarn add solana-transaction-optimizer
# or
pnpm add solana-transaction-optimizer

Overview

This library helps you optimize Solana transactions by splitting large transactions into smaller chunks. It's designed to handle Solana's transaction size limit (~1232 bytes) and optimize performance when dealing with many transactions.

Problems Solved

  • Transaction Size: Solana has a maximum limit of ~1232 bytes per transaction
  • Multiple Signers: Transactions with many signers require more space
  • Many Instructions: Transactions with many instructions can exceed size limits
  • Memory Efficiency: Avoid excessive memory usage when processing thousands of transactions

Features

  • ✅ Multiple chunking strategies for different use cases
  • ✅ Support for multiple signers
  • ✅ Adaptive sizing with automatic chunk size adjustment
  • ✅ Streaming support for memory-efficient processing
  • ✅ Parallel transaction sending
  • ✅ TypeScript support with full type definitions

Quick Start

import { buildTxChunkSmart, sendTxParallel } from 'solana-transaction-optimizer';
import { Connection, Keypair, SystemProgram, PublicKey } from '@solana/web3.js';

// Build transaction chunks
const txList = await buildTxChunkSmart(
  10, // chunkSize
  priorityFeeIx,
  transferInstructions,
  signers,
  payerKeypair,
  lookupTableAccount,
  connection
);

// Send transactions in parallel
const signatures = await sendTxParallel({
  txList,
  connection
});

API Reference

Chunking Functions

buildTxChunkManySigners

Split transactions with multiple signers into batches.

Use when:

  • Bundle wallet sweeping
  • Multi-wallet draining
  • Transactions with multiple signers (> 1)
buildTxChunkManySigners(
  chunkSize: number,
  priorityFeeIx: TransactionInstruction[],
  transferInstructions: TransactionInstruction[],
  bundlerSigners: Keypair[],
  payerKeypair: Keypair,
  lookupTableAccount: AddressLookupTableAccount,
  connection: Connection
): Promise<VersionedTransaction[]>

buildTxChunkSingleSigner

Split transactions with many instructions but only 1 signer.

Use when:

  • Sweeping many transfers from one wallet
  • Large transactions with ALT
  • Single wallet with many instructions
buildTxChunkSingleSigner(
  chunkSize: number,
  priorityFeeIx: TransactionInstruction[],
  transferInstructions: TransactionInstruction[],
  payerKeypair: Keypair,
  lookupTableAccount: AddressLookupTableAccount,
  connection: Connection
): Promise<VersionedTransaction[]>

buildTxChunkByInstruction

Split transactions based on instruction count (can have multiple signers).

Use when:

  • Batch token payments
  • Multi-token transfers
  • Complex SPL instructions
  • Many CPI calls
buildTxChunkByInstruction(
  chunkSize: number,
  priorityFeeIx: TransactionInstruction[],
  transferInstructions: TransactionInstruction[],
  signers: Keypair[],
  payerKeypair: Keypair,
  lookupTableAccount: AddressLookupTableAccount,
  connection: Connection
): Promise<VersionedTransaction[]>

buildTxChunkSmart

Automatic builder that chooses chunking strategy based on signer count.

Use when:

  • General purpose use cases
  • Dynamic use cases with varying signer counts
  • Quick implementation without thinking about strategy
buildTxChunkSmart(
  chunkSize: number,
  priorityFeeIx: TransactionInstruction[],
  transferInstructions: TransactionInstruction[],
  signers: Keypair[],
  payerKeypair: Keypair,
  lookupTableAccount: AddressLookupTableAccount,
  connection: Connection
): Promise<VersionedTransaction[]>

Logic:

  • If signers.length > 1 → uses buildTxChunkManySigners
  • If signers.length === 1 → uses buildTxChunkSingleSigner

buildTxChunkDynamic

Builder with adaptive sizing that automatically adjusts chunk size based on transaction size.

Use when:

  • Instruction sizes vary
  • Unsure about optimal chunk size
  • Need automatic optimization based on actual size
buildTxChunkDynamic({
  instructions: TransactionInstruction[],
  signers: Keypair[],
  payer: Keypair,
  alts?: AddressLookupTableAccount[],
  priorityFeeIx?: TransactionInstruction[],
  connection: Connection,
  maxBytes?: number,        // default: 1232
  initialChunkSize?: number // default: 10
}): Promise<VersionedTransaction[]>

streamTxChunksGenerator

Async generator for streaming transactions (lazy evaluation).

Use when:

  • Processing thousands of transactions
  • Memory-constrained environments
  • Streaming processing pattern
  • Don't need all transactions in memory at once
streamTxChunksGenerator({
  chunkSize: number,
  instructions: TransactionInstruction[],
  signers: Keypair[],
  payer: Keypair,
  alts?: AddressLookupTableAccount[],
  priorityFeeIx?: TransactionInstruction[],
  connection: Connection
}): AsyncGenerator<VersionedTransaction, void, unknown>

Utility Functions

sendTxParallel

Send multiple transactions in parallel.

sendTxParallel({
  txList: VersionedTransaction[],
  connection: Connection
}): Promise<string[]>

estimateGasCost

Calculate fee based on compute units and price.

estimateGasCost({
  computeUnitLimit: number,
  computeUnitPriceMicroLamports: number
}): number

calculateTxSize

Calculate transaction size in bytes.

calculateTxSize(tx: VersionedTransaction): number

Examples

Example 1: Bundle Wallet Sweeping

import { buildTxChunkManySigners, sendTxParallel } from 'solana-transaction-optimizer';
import { Connection, Keypair, SystemProgram, PublicKey, ComputeBudgetProgram } from '@solana/web3.js';

async function sweepBundleWallets(
  bundleWallets: Keypair[],
  payerKeypair: Keypair,
  destinationAddress: string,
  connection: Connection,
  lookupTableAccount: AddressLookupTableAccount
) {
  // Create transfer instructions for each wallet
  const transferInstructions = bundleWallets.map(wallet =>
    SystemProgram.transfer({
      fromPubkey: wallet.publicKey,
      toPubkey: new PublicKey(destinationAddress),
      lamports: // calculate amount
    })
  );

  // Priority fee instructions
  const priorityFeeIx = [
    ComputeBudgetProgram.setComputeUnitLimit({ units: 250_000 }),
    ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 4 })
  ];

  // Build chunks with 10 signers per batch
  const txList = await buildTxChunkManySigners(
    10, // chunkSize
    priorityFeeIx,
    transferInstructions,
    bundleWallets, // bundlerSigners
    payerKeypair,
    lookupTableAccount,
    connection
  );

  // Send all transactions in parallel
  const signatures = await sendTxParallel({
    txList,
    connection
  });

  console.log(`Sent ${signatures.length} transactions`);
  return signatures;
}

Example 2: Dynamic Chunking with Adaptive Sizing

import { buildTxChunkDynamic } from 'solana-transaction-optimizer';

async function adaptiveChunking(
  instructions: TransactionInstruction[],
  signers: Keypair[],
  payer: Keypair,
  connection: Connection,
  alts: AddressLookupTableAccount[]
) {
  // Automatically adjust chunk size
  const txList = await buildTxChunkDynamic({
    instructions,
    signers,
    payer,
    alts,
    connection,
    maxBytes: 1232,        // Maximum limit
    initialChunkSize: 15   // Start with 15 instructions
  });

  // If chunk is too large, it will automatically reduce
  // until it finds the right size
  return txList;
}

Example 3: Streaming for Large Batches

import { streamTxChunksGenerator } from 'solana-transaction-optimizer';

async function processLargeBatch(
  instructions: TransactionInstruction[],
  signers: Keypair[],
  payer: Keypair,
  connection: Connection
) {
  // Generator for streaming (doesn't load all into memory)
  const generator = streamTxChunksGenerator({
    chunkSize: 10,
    instructions,
    signers,
    payer,
    connection
  });

  // Process one by one
  for await (const tx of generator) {
    try {
      const signature = await connection.sendTransaction(tx);
      console.log(`Sent: ${signature}`);
    } catch (error) {
      console.error('Failed to send transaction:', error);
    }
  }
}

Chunking Strategy Guide

Strategy Signers Instructions Use Case
buildTxChunkManySigners > 1 Medium Bundle wallet sweeping
buildTxChunkSingleSigner 1 Many Single wallet distribution
buildTxChunkByInstruction 1 or more Many & Complex Token batch payments
buildTxChunkSmart Dynamic Dynamic General purpose
buildTxChunkDynamic Dynamic Dynamic & Varied Adaptive sizing
streamTxChunksGenerator Dynamic Very Many Memory-efficient streaming

Recommended Chunk Sizes

  • SOL Transfer: 15-25 instructions per batch
  • SPL Token Transfer: 5-10 instructions per batch
  • Multiple Signers: 5-10 signers per batch
  • Complex Instructions: 3-5 instructions per batch

Performance Tips

  1. Use ALT (Address Lookup Table): ALT can significantly reduce transaction size
  2. Batch Blockhash Requests: Reuse blockhash for multiple transactions when possible
  3. Parallel Sending: Use sendTxParallel to send multiple transactions at once
  4. Streaming for Large Batches: Use streamTxChunksGenerator for thousands of transactions
  5. Monitor Transaction Size: Use calculateTxSize to monitor transaction sizes

Requirements

  • Node.js >= 16.0.0
  • @solana/web3.js ^1.95.0

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues and questions, please open an issue on GitHub.

About

Optimize Solana transactions by chunking large transactions into smaller batches. Supports multiple signers, complex instructions, and adaptive sizing.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors