Перейти к основному содержанию

Параметры

transaction
string
обязательно
Транзакция для симуляции в виде строки в кодировке base-64
config
object
Объект конфигурации, содержащий:
sigVerify
boolean
Если true, проверить подписи транзакции
commitment
string
Уровень подтверждения (processed, confirmed, finalized)
encoding
string
Формат кодирования транзакции (base58, base64, json)
replaceRecentBlockhash
boolean
Если true, заменить недавний blockhash транзакции наиболее актуальным blockhash
accounts
object
Объект конфигурации счетов, содержащий:
encoding
string
Формат кодирования данных счета (base58, base64, json)
addresses
array
Массив адресов счетов, для которых нужно вернуть данные
minContextSlot
number
Минимальный слот, при котором может быть оценён запрос

Ответ

result
object
Объект, содержащий:
err
string | null
Ошибка, если симуляция транзакции не удалась, null если симуляция прошла успешно
logs
array
Массив сообщений журнала, выводимых инструкциями транзакции во время выполнения
accounts
array
Массив счетов с их данными (если запрошено)
unitsConsumed
number
Количество вычислительных единиц, потреблённых транзакцией
returnData
object
Возвращаемые данные программы из симуляции

Примеры кода

Базовый запрос

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

Использование web3.js

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

const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');

// 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
    }
  };
}

Примечания

  1. Симулирует отправку транзакции без фактической её отправки
  2. Ответ содержит журналы выполнения и ошибки
  3. Симуляция выполняется на текущем состоянии
  4. Изменения состояния не сохраняются
  5. Полезно для тестирования и отладки транзакций

Рекомендации

  1. Используйте симуляцию перед отправкой реальных транзакций
  2. Проверяйте потребление вычислительных единиц
  3. Анализируйте журналы программы на предмет ошибок
  4. Проверяйте состояния счетов после симуляции
  5. Обрабатывайте ошибки симуляции должным образом

Распространённые ошибки

КодСообщениеРешение
-32601Method not foundУбедитесь, что вы подключены к узлу Solana RPC
-32602Invalid paramsПроверьте формат транзакции и кодирование
-32003Transaction simulation failedПроверьте корректность транзакции и балансы счетов
-32004Blockhash not foundИспользуйте более актуальный blockhash
-32005Node is behindПопробуйте другой узел RPC

Сценарии использования

  1. Анализ транзакций
    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. Валидация транзакций
    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. Оптимизация транзакций
    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;
      }
    }