参数
完全签名的交易,以 base-64 编码的字符串表示
包含以下内容的配置对象:
交易的编码格式(base58、base64、json)
Commitment 级别(processed、confirmed、finalized)
预检的 commitment 级别(processed、confirmed、finalized)
如果为 true,跳过预检交易检查
RPC 节点重试向领导者发送交易的最大次数
请求可被评估的最小 slot
响应
交易中嵌入的第一个交易签名(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
最佳实践
- 根据需求使用适当的 commitment 级别
- 在继续之前等待交易确认
- 处理速率限制和网络错误
- 监控交易状态以检测失败
- 考虑使用预检检查进行验证
常见错误
| 错误码 | 消息 | 解决方案 |
|---|---|---|
| -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() } }; } async splitTransaction( transaction: Transaction ): Promise<Array<{ transaction: Transaction; signers: Array<PublicKey>; }>> { const chunks: Array<{ transaction: Transaction; signers: Array<PublicKey>; }> = []; let currentChunk = new Transaction(); let currentSigners: Array<PublicKey> = []; for (let i = 0; i < transaction.instructions.length; i++) { const instruction = transaction.instructions[i]; if (currentChunk.instructions.length >= this.maxInstructions) { chunks.push({ transaction: currentChunk, signers: currentSigners }); currentChunk = new Transaction(); currentSigners = []; } currentChunk.add(instruction); // Add unique signers instruction.keys .filter(key => key.isSigner) .forEach(key => { if (!currentSigners.find(signer => signer.equals(key.pubkey))) { currentSigners.push(key.pubkey); } }); } if (currentChunk.instructions.length > 0) { chunks.push({ transaction: currentChunk, signers: currentSigners }); } return chunks; } }