Параметры
Полностью подписанная транзакция в виде строки в кодировке base-64
Объект конфигурации, содержащий:Формат кодирования транзакции (base58, base64, json)
Уровень подтверждения (processed, confirmed, finalized)
Уровень подтверждения для предварительной проверки (processed, confirmed, finalized)
Если true, пропустить предварительные проверки транзакции
Максимальное количество попыток RPC-узла повторно отправить транзакцию лидеру
Минимальный слот, при котором может быть оценён запрос
Ответ
Первая подпись транзакции, встроенная в транзакцию (в кодировке base-58)
Примеры кода
Базовый запрос
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": "sendTransaction",
"params": [
"4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTFNhyKNMr3WNFCrXgDS7uV7u",
{
"encoding": "base64",
"commitment": "confirmed"
}
]
}'
Использование 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 send transaction
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: new PublicKey('83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri'),
toPubkey: new PublicKey('J7rBdM6AecPDEZp8aPq5tPmsPzPhQG4HD6YtAcQBDfJj'),
lamports: 1000000000
})
);
const signature = await connection.sendTransaction(transaction, [signer]);
console.log('Transaction signature:', signature);
// Send transaction with confirmation
async function sendTransactionWithConfirmation(
transaction: Transaction,
signers: Array<Signer>,
config: {
commitment?: string;
preflightCommitment?: string;
skipPreflight?: boolean;
maxRetries?: number;
}
) {
const signature = await connection.sendTransaction(
transaction,
signers,
config
);
// Wait for confirmation
const confirmation = await connection.confirmTransaction(signature, config.commitment);
return {
signature,
confirmation: {
status: confirmation.value.err ? 'failed' : 'success',
slot: confirmation.context.slot
},
transaction: {
recentBlockhash: transaction.recentBlockhash,
feePayer: transaction.feePayer?.toBase58(),
instructions: transaction.instructions.length
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
Примечания
- Отправляет подписанную транзакцию в кластер
- Транзакция должна быть полностью подписана
- Ответ — это подпись транзакции
- Транзакция должна быть подтверждена перед применением изменений
- Транзакция должна содержать актуальный blockhash
Рекомендации
- Используйте подходящий уровень подтверждения в зависимости от ваших потребностей
- Дождитесь подтверждения транзакции перед продолжением работы
- Обрабатывайте ограничения запросов и сетевые ошибки
- Отслеживайте статус транзакции на предмет сбоев
- Рассмотрите использование предварительных проверок для валидации
Распространённые ошибки
| Код | Сообщение | Решение |
|---|
| -32601 | Method not found | Убедитесь, что вы подключены к узлу Solana RPC |
| -32602 | Invalid params | Проверьте формат транзакции и кодирование |
| -32003 | Transaction simulation failed | Проверьте корректность транзакции и балансы счетов |
| -32004 | Blockhash not found | Используйте более актуальный blockhash |
| -32005 | Node is behind | Попробуйте другой узел RPC |
| -32006 | Transaction already processed | Транзакция уже была отправлена |
| -32007 | Transaction signature verification failed | Проверьте подписи транзакции |
Сценарии использования
-
Управление транзакциями
interface TransactionRequest {
transaction: {
signature: string;
blockhash: string;
feePayer: string;
instructions: number;
};
status: {
state: 'pending' | 'success' | 'failed';
confirmations: number;
slot?: number;
error?: string;
};
metadata: {
timestamp: number;
commitment?: string;
retries: number;
};
}
class TransactionManager {
private readonly maxRetries = 3;
private readonly confirmationTimeout = 30000; // 30 seconds
async sendTransaction(
transaction: Transaction,
signers: Array<Signer>,
config: {
commitment?: string;
preflightCommitment?: string;
skipPreflight?: boolean;
}
): Promise<TransactionRequest> {
let retries = 0;
let signature: string;
while (retries < this.maxRetries) {
try {
// Get a new blockhash if retrying
if (retries > 0) {
const { blockhash } = await connection.getRecentBlockhash();
transaction.recentBlockhash = blockhash;
}
signature = await connection.sendTransaction(
transaction,
signers,
config
);
// Wait for confirmation
const confirmation = await connection.confirmTransaction(signature, config.commitment);
if (!confirmation.value.err) {
return {
transaction: {
signature,
blockhash: transaction.recentBlockhash,
feePayer: transaction.feePayer?.toBase58(),
instructions: transaction.instructions.length
},
status: {
state: 'success',
confirmations: 1,
slot: confirmation.context.slot
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment,
retries
}
};
}
throw new Error(confirmation.value.err.toString());
} catch (error) {
retries++;
if (retries === this.maxRetries) {
return {
transaction: {
signature,
blockhash: transaction.recentBlockhash,
feePayer: transaction.feePayer?.toBase58(),
instructions: transaction.instructions.length
},
status: {
state: 'failed',
confirmations: 0,
error: error.message
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment,
retries
}
};
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
}
-
Мониторинг транзакций
interface TransactionStatus {
transaction: {
signature: string;
slot?: number;
};
status: {
confirmations: number;
err: any;
};
history: Array<{
timestamp: number;
confirmations: number;
error?: string;
}>;
}
class TransactionMonitor {
private transactions: Map<string, TransactionStatus> = new Map();
async monitorTransaction(
signature: string
): Promise<TransactionStatus> {
let status = this.transactions.get(signature);
const now = Date.now();
const confirmation = await connection.getSignatureStatus(signature);
if (!status) {
status = {
transaction: {
signature,
slot: confirmation?.slot
},
status: {
confirmations: confirmation?.confirmations || 0,
err: confirmation?.err
},
history: [{
timestamp: now,
confirmations: confirmation?.confirmations || 0,
error: confirmation?.err?.toString()
}]
};
} else {
status.status.confirmations = confirmation?.confirmations || 0;
status.status.err = confirmation?.err;
status.history.push({
timestamp: now,
confirmations: confirmation?.confirmations || 0,
error: confirmation?.err?.toString()
});
}
this.transactions.set(signature, status);
return status;
}
}
-
Планирование транзакций
interface TransactionPlan {
transaction: {
instructions: number;
signers: number;
estimatedFee: number;
};
recommendations: Array<{
type: 'proceed' | 'split' | 'abort';
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class TransactionPlanner {
private readonly maxInstructions = 10;
private readonly maxSigners = 8;
async planTransaction(
transaction: Transaction
): Promise<TransactionPlan> {
const { feeCalculator } = await connection.getRecentBlockhash();
const estimatedFee = feeCalculator.lamportsPerSignature * transaction.signatures.length;
const recommendations: Array<{
type: 'proceed' | 'split' | 'abort';
reason: string;
}> = [];
if (transaction.instructions.length > this.maxInstructions) {
recommendations.push({
type: 'split',
reason: `Transaction has ${transaction.instructions.length} instructions, exceeding maximum of ${this.maxInstructions}`
});
}
if (transaction.signatures.length > this.maxSigners) {
recommendations.push({
type: 'split',
reason: `Transaction has ${transaction.signatures.length} signers, exceeding maximum of ${this.maxSigners}`
});
}
if (recommendations.length === 0) {
recommendations.push({
type: 'proceed',
reason: 'Transaction is within limits'
});
}
return {
transaction: {
instructions: transaction.instructions.length,
signers: transaction.signatures.length,
estimatedFee
},
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
}