跳转到主要内容

参数

transaction
string
必填
要模拟的交易,以 base-64 编码的字符串表示
config
object
包含以下内容的配置对象:
sigVerify
boolean
如果为 true,验证交易签名
commitment
string
Commitment 级别(processed、confirmed、finalized)
encoding
string
交易的编码格式(base58、base64、json)
replaceRecentBlockhash
boolean
如果为 true,将交易的近期 blockhash 替换为最新的 blockhash
accounts
object
账户配置对象,包含:
encoding
string
账户数据的编码格式(base58、base64、json)
addresses
array
要返回数据的账户地址数组
minContextSlot
number
请求可被评估的最小 slot

响应

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;
        }> = [];
        
        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'
          });
        }
        
        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}`
          });
        }
        
        const logs = simulation.value.logs || [];
        checks.push({
          name: 'Program Execution',
          passed: logs.some(log => log.includes('Program log:')),
          details: 'Program execution logs present'
        });
        
        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> {
        const originalSimulation = await connection.simulateTransaction(transaction);
        const originalUnits = originalSimulation.value.unitsConsumed || 0;
        
        const changes: Array<{
          type: 'merge' | 'remove' | 'reorder';
          description: string;
          impact: number;
        }> = [];
        
        const optimizedTransaction = new Transaction();
        const instructions = [...transaction.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
          });
        }
        
        const cleanedInstructions = this.removeRedundantInstructions(mergedInstructions);
        if (cleanedInstructions.length < mergedInstructions.length) {
          changes.push({
            type: 'remove',
            description: `Removed ${mergedInstructions.length - cleanedInstructions.length} redundant instructions`,
            impact: 0
          });
        }
        
        const orderedInstructions = this.reorderInstructions(cleanedInstructions);
        changes.push({
          type: 'reorder',
          description: 'Reordered instructions for optimal execution',
          impact: 0
        });
        
        orderedInstructions.forEach(instruction => {
          optimizedTransaction.add(instruction);
        });
        
        const optimizedSimulation = await connection.simulateTransaction(optimizedTransaction);
        const optimizedUnits = optimizedSimulation.value.unitsConsumed || 0;
        
        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> {
        return instructions;
      }
      
      private removeRedundantInstructions(instructions: Array<any>): Array<any> {
        return instructions;
      }
      
      private reorderInstructions(instructions: Array<any>): Array<any> {
        return instructions;
      }
    }