Параметры
Транзакция для симуляции в виде строки в кодировке base-64
Объект конфигурации, содержащий:Если true, проверить подписи транзакции
Уровень подтверждения (processed, confirmed, finalized)
Формат кодирования транзакции (base58, base64, json)
Если true, заменить недавний blockhash транзакции наиболее актуальным blockhash
Объект конфигурации счетов, содержащий:Формат кодирования данных счета (base58, base64, json)
Массив адресов счетов, для которых нужно вернуть данные
Минимальный слот, при котором может быть оценён запрос
Ответ
Объект, содержащий:Ошибка, если симуляция транзакции не удалась, null если симуляция прошла успешно
Массив сообщений журнала, выводимых инструкциями транзакции во время выполнения
Массив счетов с их данными (если запрошено)
Количество вычислительных единиц, потреблённых транзакцией
Возвращаемые данные программы из симуляции
Примеры кода
Базовый запрос
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;
}> = [];
// 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()
}
};
}
}
-
Оптимизация транзакций
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;
}
}