跳转到主要内容

参数

transaction
string
必填
完全签名的交易,以 base-64 编码的字符串表示
config
object
包含以下内容的配置对象:
encoding
string
交易的编码格式(base58、base64、json)
commitment
string
Commitment 级别(processed、confirmed、finalized)
preflightCommitment
string
预检的 commitment 级别(processed、confirmed、finalized)
skipPreflight
boolean
如果为 true,跳过预检交易检查
maxRetries
number
RPC 节点重试向领导者发送交易的最大次数
minContextSlot
number
请求可被评估的最小 slot

响应

result
string
交易中嵌入的第一个交易签名(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
    }
  };
}

注意事项

  1. 将已签名的交易提交到集群
  2. 交易必须完全签名
  3. 响应是交易签名
  4. 交易必须被确认后更改才会生效
  5. 交易必须包含近期的 blockhash

最佳实践

  1. 根据需求使用适当的 commitment 级别
  2. 在继续之前等待交易确认
  3. 处理速率限制和网络错误
  4. 监控交易状态以检测失败
  5. 考虑使用预检检查进行验证

常见错误

错误码消息解决方案
-32601Method not found验证是否连接到 Solana RPC 节点
-32602Invalid params检查交易格式和编码
-32003Transaction simulation failed验证交易有效性和账户余额
-32004Blockhash not found使用更近期的 blockhash
-32005Node is behind尝试其他 RPC 节点
-32006Transaction already processed交易已被提交
-32007Transaction signature verification failed检查交易签名

用例

  1. 交易管理
    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));
          }
        }
      }
    }
    
  2. 交易监控
    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;
      }
    }
    
  3. 交易规划
    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;
      }
    }