Parameters

transaction
string
required

Transaction to simulate, as a base-64 encoded string

config
object

Configuration object containing:

sigVerify
boolean

If true, verify transaction signatures

commitment
string

Commitment level (processed, confirmed, finalized)

encoding
string

Encoding format for the transaction (base58, base64, json)

replaceRecentBlockhash
boolean

If true, replace the transaction’s recent blockhash with the most recent blockhash

accounts
object

Accounts configuration object containing:

encoding
string

Encoding format for account data (base58, base64, json)

addresses
array

Array of account addresses to return data for

minContextSlot
number

The minimum slot that the request can be evaluated at

Response

result
object

Object containing:

err
string | null

Error if transaction simulation failed, null if simulation succeeded

logs
array

Array of log messages the transaction instructions output during execution

accounts
array

Array of accounts with their data (if requested)

unitsConsumed
number

Number of compute units consumed by the transaction

returnData
object

Program return data from the simulation

Code Examples

Basic Request

curl https://rpc.orbitflare.com -X POST -H "Content-Type: application/json" -d '{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "simulateTransaction",
  "params": [
    "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTFNhyKNMr3WNFCrXgDS7uV7u",
    {
      "sigVerify": false,
      "commitment": "confirmed",
      "encoding": "base64"
    }
  ]
}'

Using web3.js

import { Connection, Transaction, PublicKey, SystemProgram } from '@solana/web3.js';

const connection = new Connection('https://rpc.orbitflare.com');

// Create and simulate transaction
const transaction = new Transaction().add(
  SystemProgram.transfer({
    fromPubkey: new PublicKey('83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri'),
    toPubkey: new PublicKey('J7rBdM6AecPDEZp8aPq5tPmsPzPhQG4HD6YtAcQBDfJj'),
    lamports: 1000000000
  })
);

const simulation = await connection.simulateTransaction(transaction);
console.log('Simulation result:', simulation);

// Simulate transaction with analysis
async function simulateTransactionWithAnalysis(
  transaction: Transaction,
  config: { 
    sigVerify?: boolean;
    commitment?: string;
    replaceRecentBlockhash?: boolean;
  }
) {
  const simulation = await connection.simulateTransaction(transaction, config);
  
  return {
    simulation: {
      success: !simulation.value.err,
      error: simulation.value.err,
      logs: simulation.value.logs,
      unitsConsumed: simulation.value.unitsConsumed
    },
    transaction: {
      recentBlockhash: transaction.recentBlockhash,
      feePayer: transaction.feePayer?.toBase58(),
      instructions: transaction.instructions.length
    },
    metadata: {
      timestamp: Date.now(),
      commitment: config.commitment
    }
  };
}

Notes

  1. Simulates sending a transaction without actually sending it
  2. The response includes execution logs and errors
  3. The simulation is performed on the current state
  4. No state changes are persisted
  5. Useful for testing and debugging transactions

Best Practices

  1. Use simulation before sending real transactions
  2. Check compute unit consumption
  3. Analyze program logs for errors
  4. Verify account states after simulation
  5. Handle simulation errors appropriately

Common Errors

CodeMessageSolution
-32601Method not foundVerify you’re connected to a Solana RPC node
-32602Invalid paramsCheck transaction format and encoding
-32003Transaction simulation failedVerify transaction validity and account balances
-32004Blockhash not foundUse a more recent blockhash
-32005Node is behindTry a different RPC node

Use Cases

  1. Transaction Analysis

    interface TransactionAnalysis {
      transaction: {
        instructions: number;
        signers: number;
        estimatedUnits: number;
      };
      simulation: {
        success: boolean;
        error: string | null;
        logs: string[];
        unitsConsumed: number;
      };
      analysis: {
        programInvocations: number;
        accountAccesses: number;
        errorAnalysis?: string;
      };
      metadata: {
        timestamp: number;
        commitment?: string;
      };
    }
    
    class TransactionAnalyzer {
      async analyzeTransaction(
        transaction: Transaction,
        config: { 
          sigVerify?: boolean;
          commitment?: string;
        }
      ): Promise<TransactionAnalysis> {
        const simulation = await connection.simulateTransaction(transaction, config);
        
        const logs = simulation.value.logs || [];
        const programInvocations = logs.filter(log => 
          log.includes('Program') && log.includes('invoke')
        ).length;
        
        const accountAccesses = logs.filter(log =>
          log.includes('Account') || log.includes('Token')
        ).length;
        
        let errorAnalysis;
        if (simulation.value.err) {
          errorAnalysis = this.analyzeError(simulation.value.err);
        }
        
        return {
          transaction: {
            instructions: transaction.instructions.length,
            signers: transaction.signatures.length,
            estimatedUnits: simulation.value.unitsConsumed || 0
          },
          simulation: {
            success: !simulation.value.err,
            error: simulation.value.err,
            logs,
            unitsConsumed: simulation.value.unitsConsumed || 0
          },
          analysis: {
            programInvocations,
            accountAccesses,
            errorAnalysis
          },
          metadata: {
            timestamp: Date.now(),
            commitment: config.commitment
          }
        };
      }
      
      private analyzeError(error: any): string {
        if (typeof error === 'string') {
          if (error.includes('insufficient funds')) {
            return 'Transaction failed due to insufficient funds';
          }
          if (error.includes('invalid account owner')) {
            return 'Transaction failed due to invalid account ownership';
          }
          if (error.includes('account not found')) {
            return 'Transaction failed due to missing account';
          }
        }
        return 'Unknown error occurred during simulation';
      }
    }
    
  2. Transaction Validation

    interface TransactionValidation {
      transaction: {
        signature: string;
        blockhash: string;
        feePayer: string;
      };
      checks: Array<{
        name: string;
        passed: boolean;
        details?: string;
      }>;
      recommendations: Array<{
        type: 'fix' | 'warning' | 'info';
        message: string;
      }>;
      metadata: {
        timestamp: number;
      };
    }
    
    class TransactionValidator {
      private readonly maxUnits = 200000;
      private readonly minAccountRent = 890880;
      
      async validateTransaction(
        transaction: Transaction,
        config: { 
          sigVerify?: boolean;
          commitment?: string;
        }
      ): Promise<TransactionValidation> {
        const simulation = await connection.simulateTransaction(transaction, config);
        
        const checks: Array<{
          name: string;
          passed: boolean;
          details?: string;
        }> = [];
        
        const recommendations: Array<{
          type: 'fix' | 'warning' | 'info';
          message: string;
        }> = [];
        
        // Check compute units
        const unitsConsumed = simulation.value.unitsConsumed || 0;
        checks.push({
          name: 'Compute Units',
          passed: unitsConsumed <= this.maxUnits,
          details: `${unitsConsumed} units consumed`
        });
        
        if (unitsConsumed > this.maxUnits) {
          recommendations.push({
            type: 'fix',
            message: 'Transaction exceeds compute unit limit'
          });
        }
        
        // Check for errors
        checks.push({
          name: 'Simulation',
          passed: !simulation.value.err,
          details: simulation.value.err?.toString()
        });
        
        if (simulation.value.err) {
          recommendations.push({
            type: 'fix',
            message: `Fix simulation error: ${simulation.value.err}`
          });
        }
        
        // Analyze logs
        const logs = simulation.value.logs || [];
        checks.push({
          name: 'Program Execution',
          passed: logs.some(log => log.includes('Program log:')),
          details: 'Program execution logs present'
        });
        
        // Check account rent
        const rentWarnings = logs.filter(log => 
          log.includes('insufficient lamports for rent')
        );
        
        if (rentWarnings.length > 0) {
          checks.push({
            name: 'Account Rent',
            passed: false,
            details: 'Insufficient lamports for rent'
          });
          
          recommendations.push({
            type: 'fix',
            message: `Ensure accounts have minimum rent: ${this.minAccountRent} lamports`
          });
        }
        
        return {
          transaction: {
            signature: transaction.signatures[0]?.signature?.toString() || '',
            blockhash: transaction.recentBlockhash || '',
            feePayer: transaction.feePayer?.toBase58() || ''
          },
          checks,
          recommendations,
          metadata: {
            timestamp: Date.now()
          }
        };
      }
    }
    
  3. Transaction Optimization

    interface TransactionOptimization {
      original: {
        instructions: number;
        unitsConsumed: number;
      };
      optimized: {
        instructions: number;
        unitsConsumed: number;
        savings: number;
      };
      changes: Array<{
        type: 'merge' | 'remove' | 'reorder';
        description: string;
        impact: number;
      }>;
      metadata: {
        timestamp: number;
      };
    }
    
    class TransactionOptimizer {
      async optimizeTransaction(
        transaction: Transaction
      ): Promise<TransactionOptimization> {
        // Simulate original transaction
        const originalSimulation = await connection.simulateTransaction(transaction);
        const originalUnits = originalSimulation.value.unitsConsumed || 0;
        
        const changes: Array<{
          type: 'merge' | 'remove' | 'reorder';
          description: string;
          impact: number;
        }> = [];
        
        // Create optimized transaction
        const optimizedTransaction = new Transaction();
        const instructions = [...transaction.instructions];
        
        // Analyze and merge similar instructions
        const mergedInstructions = this.mergeInstructions(instructions);
        if (mergedInstructions.length < instructions.length) {
          changes.push({
            type: 'merge',
            description: `Merged ${instructions.length - mergedInstructions.length} similar instructions`,
            impact: 0 // Will be updated after simulation
          });
        }
        
        // Remove redundant instructions
        const cleanedInstructions = this.removeRedundantInstructions(mergedInstructions);
        if (cleanedInstructions.length < mergedInstructions.length) {
          changes.push({
            type: 'remove',
            description: `Removed ${mergedInstructions.length - cleanedInstructions.length} redundant instructions`,
            impact: 0
          });
        }
        
        // Reorder instructions for optimal execution
        const orderedInstructions = this.reorderInstructions(cleanedInstructions);
        changes.push({
          type: 'reorder',
          description: 'Reordered instructions for optimal execution',
          impact: 0
        });
        
        // Add optimized instructions
        orderedInstructions.forEach(instruction => {
          optimizedTransaction.add(instruction);
        });
        
        // Simulate optimized transaction
        const optimizedSimulation = await connection.simulateTransaction(optimizedTransaction);
        const optimizedUnits = optimizedSimulation.value.unitsConsumed || 0;
        
        // Calculate impacts
        const totalSavings = originalUnits - optimizedUnits;
        changes.forEach(change => {
          change.impact = Math.floor(totalSavings / changes.length);
        });
        
        return {
          original: {
            instructions: instructions.length,
            unitsConsumed: originalUnits
          },
          optimized: {
            instructions: orderedInstructions.length,
            unitsConsumed: optimizedUnits,
            savings: totalSavings
          },
          changes,
          metadata: {
            timestamp: Date.now()
          }
        };
      }
      
      private mergeInstructions(
        instructions: Array<any>
      ): Array<any> {
        // Implementation would analyze and merge similar instructions
        // This is a placeholder that returns the original instructions
        return instructions;
      }
      
      private removeRedundantInstructions(
        instructions: Array<any>
      ): Array<any> {
        // Implementation would identify and remove redundant instructions
        // This is a placeholder that returns the original instructions
        return instructions;
      }
      
      private reorderInstructions(
        instructions: Array<any>
      ): Array<any> {
        // Implementation would reorder instructions for optimal execution
        // This is a placeholder that returns the original instructions
        return instructions;
      }
    }