参数
要模拟的交易,以 base-64 编码的字符串表示
包含以下内容的配置对象:
如果为 true,验证交易签名
Commitment 级别(processed、confirmed、finalized)
交易的编码格式(base58、base64、json)
如果为 true,将交易的近期 blockhash 替换为最新的 blockhash
请求可被评估的最小 slot
响应
代码示例
基本请求
复制
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
}
};
}
注意事项
- 模拟发送交易但不实际发送
- 响应包含执行日志和错误信息
- 模拟在当前状态上执行
- 不会持久化任何状态变化
- 对于测试和调试交易非常有用
最佳实践
- 在发送真实交易之前使用模拟
- 检查计算单元消耗
- 分析程序日志中的错误
- 模拟后验证账户状态
- 适当处理模拟错误
常见错误
| 错误码 | 消息 | 解决方案 |
|---|---|---|
| -32601 | Method not found | 验证是否连接到 Solana RPC 节点 |
| -32602 | Invalid params | 检查交易格式和编码 |
| -32003 | Transaction simulation failed | 验证交易有效性和账户余额 |
| -32004 | Blockhash not found | 使用更近期的 blockhash |
| -32005 | Node is behind | 尝试其他 RPC 节点 |
用例
-
交易分析
复制
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'; } } -
交易验证
复制
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() } }; } } -
交易优化
复制
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; } }