以太坊 RPC API 實務整合完整指南:從基礎操作到企業級應用架構

本文深入探討以太坊 RPC API 的實務整合技術,涵蓋從基礎的 JSON-RPC 呼叫到企業級節點架構設計。隨著 2026 年 Pectra 升級完成和 EIP-7702 的實施,RPC API 使用場景更加多元,包括智慧合約錢包交互、批量交易處理、跨鏈橋接等高階應用。本指南提供完整的 TypeScript 和 Python 程式碼範例和架構建議,幫助開發者構建高效、可靠的以太坊應用。

以太坊 RPC API 實務整合完整指南:從基礎操作到企業級應用架構

前言

以太坊 RPC API(Remote Procedure Call API)是以太坊網路與外部應用程式之間溝通的核心橋樑。自 2015 年以太坊主網上線以來,RPC API 經歷了多次重大演進。2026 年的 Pectra 升級為 RPC 生態系統帶來了革命性的變化,特別是 EIP-7702 的實施,使得智慧合約錢包能夠直接透過 RPC 進行更複雜的操作。

本文將深入探討以太坊 RPC API 的實務整合技術,涵蓋從基礎的 JSON-RPC 呼叫到企業級節點架構設計,提供完整的技術細節、程式碼範例和最佳實踐。無論您是區塊鏈新手開發者還是經驗豐富的工程師,都能從本指南獲得實用的技術知識。

第一章:RPC API 基礎理論

1.1 什麼是 JSON-RPC

JSON-RPC 是一種輕量級的遠端程序呼叫協議,使用 JSON(JavaScript Object Notation)作為資料格式。在以太坊生態系統中,所有與區塊鏈的交互都透過這種協議進行通訊。與 RESTful API 不同,JSON-RPC 是無狀態的,每個請求-響應對是完全獨立的。

以太坊採用的是 JSON-RPC 2.0 規範,這意味著所有請求都必須包含特定格式的 JSON 物件。典型的請求結構包含以下欄位:

{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": ["0x742d35Cc6634C0532925a3b844Bc9e7595f2fE5d", "latest"],
  "id": 1
}

其中 jsonrpc 欄位固定為 "2.0",表示遵循 JSON-RPC 2.0 規範;method 欄位指定要呼叫的方法名稱;params 陣列包含該方法所需的參數;id 欄位用於匹配請求與響應。

響應格式同樣遵循標準結構:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0234c8a3397aab58"
}

若發生錯誤,響應將包含 error 物件而非 result

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": "Invalid address format"
  }
}

1.2 以太坊 RPC 端點架構

以太坊網路中存在兩種主要的 RPC 端點類型:執行層 RPC(Execution RPC)和共識層 RPC(Consensus RPC)。自 2022 年 The Merge 升級後,這種區分變得更加重要。

執行層 RPC(也稱為 ETH1 RPC)主要負責處理交易和智慧合約操作。標準端口為 8545(HTTP)或 8546(WSS)。主要方法包括:

共識層 RPC(也稱為 ETH2 或 Beacon RPC)負責處理區塊提議、共識機制和驗證者操作。標準端口為 3500(HTTP)或 4000(WSS)。主要方法包括:

1.3 HTTP 與 WebSocket 傳輸層比較

選擇適當的傳輸層對於應用程式效能至關重要。HTTP/1.1、HTTPS、HTTP/2 和 WebSocket(WSS)是目前最常用的傳輸方式。

HTTP/HTTPS 連接具有以下特點:

WebSocket 連接的優勢則體現在:

對於需要監控新區塊、待處理交易或錢包餘額變化的應用,WebSocket 是首選方案。以下是 WebSocket 連接的典型使用場景:

// TypeScript 範例:使用 WebSocket 訂閱新區塊
import { WebSocket } from 'ws';

const ws = new WebSocket('wss://mainnet.infura.io/ws/v3/YOUR_PROJECT_ID');

const subscription = {
  jsonrpc: '2.0',
  method: 'eth_subscribe',
  params: ['newHeads'],
  id: 1
};

ws.on('open', () => {
  ws.send(JSON.stringify(subscription));
});

ws.on('message', (data) => {
  const response = JSON.parse(data.toString());
  if (response.params && response.params.result) {
    const block = response.params.result;
    console.log(`新區塊: #${parseInt(block.number, 16)}`);
    console.log(`區塊雜湊: ${block.hash}`);
    console.log(`時間戳: ${parseInt(block.timestamp, 16)}`);
  }
});

第二章:核心 RPC 方法詳解

2.1 區塊與交易查詢方法

ethgetBlockByNumber / ethgetBlockByHash

這兩個方法用於取得區塊的完整資訊,是最常用的查詢方法之一。eth_getBlockByNumber 接受區塊編號或特殊標籤(如 "latest"、"earliest"、"pending"),而 eth_getBlockByHash 直接使用區塊雜湊值進行查詢。

// 查詢最新區塊資訊
async function getLatestBlock(ethersProvider: ethers.JsonRpcProvider) {
  const blockNumber = await ethersProvider.getBlockNumber();
  const block = await ethersProvider.getBlock(blockNumber);
  
  return {
    number: block.number,
    hash: block.hash,
    parentHash: block.parentHash,
    timestamp: block.timestamp,
    transactions: block.transactions.length,
    gasUsed: block.gasUsed.toString(),
    gasLimit: block.gasLimit.toString()
  };
}

// 查詢特定區塊的完整交易詳情
async function getBlockWithTransactions(provider: ethers.JsonRpcProvider, blockNumber: number) {
  const block = await provider.getBlock(blockNumber, true);
  
  if (!block) {
    throw new Error(`區塊 ${blockNumber} 不存在`);
  }
  
  // 取得每筆交易的收據
  const receipts = await Promise.all(
    block.transactions.map(async (txHash: string) => {
      return await provider.getTransactionReceipt(txHash);
    })
  );
  
  return {
    block,
    receipts
  };
}

eth_getTransactionByHash

查詢單筆交易的詳細資訊,包括發送者、接收者、金額、Gas 設定等。

interface TransactionDetails {
  hash: string;
  blockNumber: number | null;
  blockHash: string | null;
  from: string;
  to: string | null;
  value: string;
  gas: string;
  gasPrice: string;
  maxFeePerGas?: string;
  maxPriorityFeePerGas?: string;
  nonce: number;
  transactionIndex: number | null;
  input: string;
  v: number;
  r: string;
  s: string;
}

async function getTransactionDetails(
  provider: ethers.JsonRpcProvider, 
  txHash: string
): Promise<TransactionDetails | null> {
  const tx = await provider.getTransaction(txHash);
  
  if (!tx) {
    return null;
  }
  
  return {
    hash: tx.hash,
    blockNumber: tx.blockNumber,
    blockHash: tx.blockHash,
    from: tx.from,
    to: tx.to,
    value: tx.value.toString(),
    gas: tx.gasLimit.toString(),
    gasPrice: tx.gasPrice?.toString() || '',
    maxFeePerGas: tx.maxFeePerGas?.toString(),
    maxPriorityFeePerGas: tx.maxPriorityFeePerGas?.toString(),
    nonce: tx.nonce,
    transactionIndex: tx.transactionIndex,
    input: tx.data,
    v: tx.v,
    r: tx.signature?.r.toString() || '',
    s: tx.signature?.s.toString() || ''
  };
}

2.2 餘額與狀態查詢方法

eth_getBalance

查詢指定地址的以太幣餘額。這是以太坊錢包應用中最基本的方法之一。

async function getWalletBalances(
  provider: ethers.JsonRpcProvider,
  addresses: string[]
): Promise<Map<string, string>> {
  const balances = new Map<string, string>();
  
  // 使用 batch 請求提高效率
  const promises = addresses.map(async (address) => {
    const balance = await provider.getBalance(address);
    balances.set(address, ethers.formatEther(balance));
  });
  
  await Promise.all(promises);
  return balances;
}

// 查詢多個地址的 ERC-20 代幣餘額
async function getERC20Balances(
  provider: ethers.JsonRpcProvider,
  tokenAddress: string,
  walletAddresses: string[]
): Promise<Map<string, bigint>> {
  const ERC20_ABI = [
    "function balanceOf(address owner) view returns (uint256)"
  ];
  
  const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
  const balances = new Map<string, bigint>();
  
  const promises = walletAddresses.map(async (address) => {
    const balance = await tokenContract.balanceOf(address);
    balances.set(address, balance);
  });
  
  await Promise.all(promises);
  return balances;
}

ethcall 與 ethestimateGas

eth_call 用於在不修改區塊鏈狀態的情況下執行智慧合約讀取操作,這對於查詢合約內部狀態非常有用。eth_estimateGas 則用於估算交易所需的 Gas 數量。

// 使用 eth_call 查詢智慧合約狀態
async function queryContractState(
  provider: ethers.JsonRpcProvider,
  contractAddress: string,
  functionName: string,
  params: any[]
): Promise<any> {
  const iface = new ethers.Interface([
    `function ${functionName}(${/* 根據實際函數簽名定義 */})`
  ]);
  
  const data = iface.encodeFunctionData(functionName, params);
  
  const result = await provider.call({
    to: contractAddress,
    data: data
  });
  
  return iface.decodeFunctionResult(functionName, result);
}

// 估算交易 Gas
async function estimateTransactionGas(
  provider: ethers.JsonRpcProvider,
  from: string,
  to: string,
  value: bigint,
  data: string
): Promise<{ gasLimit: bigint; success: boolean }> {
  try {
    const gasEstimate = await provider.estimateGas({
      from: from,
      to: to,
      value: value,
      data: data
    });
    
    // 增加 20% 的安全邊界
    const gasLimit = (gasEstimate * 120n) / 100n;
    
    return { gasLimit, success: true };
  } catch (error) {
    console.error('Gas 估算失敗:', error);
    return { gasLimit: 21000n, success: false };
  }
}

2.3 交易廣播方法

eth_sendRawTransaction

將已簽署的交易提交到網路。這是去中心化應用程式中最重要的方法之一。

interface SignedTransaction {
  from: string;
  nonce: number;
  gasLimit: bigint;
  maxFeePerGas: bigint;
  maxPriorityFeePerGas: bigint;
  to: string;
  value: bigint;
  data: string;
  chainId: number;
  signature: { v: number; r: string; s: string };
}

async function broadcastTransaction(
  provider: ethers.JsonRpcProvider,
  signedTx: SignedTransaction
): Promise<string> {
  const tx = {
    type: 2, // EIP-1559 交易類型
    chainId: signedTx.chainId,
    nonce: signedTx.nonce,
    maxFeePerGas: signedTx.maxFeePerGas,
    maxPriorityFeePerGas: signedTx.maxPriorityFeePerGas,
    gasLimit: signedTx.gasLimit,
    to: signedTx.to,
    value: signedTx.value,
    data: signedTx.data,
    v: signedTx.signature.v,
    r: signedTx.signature.r,
    s: signedTx.signature.s
  };
  
  const serializedTx = ethers.Transaction.from(tx).serialized;
  const txHash = await provider.send('eth_sendRawTransaction', [serializedTx]);
  
  return txHash;
}

// 等待交易確認
async function waitForTransaction(
  provider: ethers.JsonRpcProvider,
  txHash: string,
  confirmations: number = 1
): Promise<ethers.TransactionReceipt | null> {
  try {
    const receipt = await provider.waitForTransaction(txHash, confirmations);
    return receipt;
  } catch (error) {
    console.error('交易確認失敗:', error);
    return null;
  }
}

第三章:企業級 RPC 架構設計

3.1 節點部署策略

企業級應用需要高可用性和低延遲的 RPC 服務。以下是推薦的架構模式:

多節點負載均衡架構

                    ┌─────────────────┐
                    │   負載均衡器    │
                    │   (Nginx/AWS)   │
                    └────────┬────────┘
                             │
         ┌───────────────────┼───────────────────┐
         │                   │                   │
    ┌────▼────┐        ┌────▼────┐        ┌────▼────┐
    │ 節點 1  │        │ 節點 2  │        │ 節點 3  │
    │ (Geth)  │        │ (Reth)  │        │ (Besu)  │
    └─────────┘        └─────────┘        └─────────┘

這種架構確保單一節點故障不會影響整體服務可用性。推薦使用 Ansible 或 Terraform 進行基礎設施自動化。

# Terraform 配置範例:部署高可用 RPC 節點
resource "aws_instance" "ethereum_node" {
  count         = 3
  ami           = "ami-0c55b159cbfafe1f0"  # Ubuntu 22.04
  instance_type = "m6i.2xlarge"
  
  root_block_device {
    volume_size = 500
    volume_type = "gp3"
    throughput  = 1000
  }
  
  tags = {
    Name = "ethereum-rpc-node-${count.index}"
    Role = "rpc_validator"
  }
  
  user_data = <<-EOF
              #!/bin/bash
              apt-get update
              apt-get install -y docker.io docker-compose
              systemctl start docker
              EOF
}

resource "aws_lb" "rpc_lb" {
  name               = "ethereum-rpc-lb"
  internal           = false
  load_balancer_type = "network"
  subnets            = aws_subnet.public[*].id
}

resource "aws_lb_target_group" "rpc_tg" {
  name     = "ethereum-rpc-tg"
  port     = 8545
  protocol = "TCP"
  vpc_id   = aws_vpc.main.id
}

resource "aws_lb_target_group_attachment" "rpc_attach" {
  count = 3
  target_group_arn = aws_lb_target_group.rpc_tg.arn
  target_id        = aws_instance.ethereum_node[count.index].id
  port             = 8545
}

快取層設計

對於讀取密集型應用,引入 Redis 快取層可以大幅降低 RPC 節點負載:

import Redis from 'ioredis';

interface CacheConfig {
  ttl: number;
  prefix: string;
}

class RPCCache {
  private redis: Redis;
  private ttl: number;
  private prefix: string;
  
  constructor(redisUrl: string, config: CacheConfig) {
    this.redis = new Redis(redisUrl);
    this.ttl = config.ttl;
    this.prefix = config.prefix;
  }
  
  private getCacheKey(method: string, params: any[]): string {
    return `${this.prefix}:${method}:${JSON.stringify(params)}`;
  }
  
  async get<T>(method: string, params: any[]): Promise<T | null> {
    const key = this.getCacheKey(method, params);
    const cached = await this.redis.get(key);
    
    if (cached) {
      return JSON.parse(cached);
    }
    return null;
  }
  
  async set(method: string, params: any[], value: any, ttl?: number): Promise<void> {
    const key = this.getCacheKey(method, params);
    await this.redis.setex(key, ttl || this.ttl, JSON.stringify(value));
  }
  
  async invalidate(pattern: string): Promise<void> {
    const keys = await this.redis.keys(`${this.prefix}:${pattern}`);
    if (keys.length > 0) {
      await this.redis.del(...keys);
    }
  }
}

// 使用快取層封裝 RPC 提供者
class CachedRPCProvider {
  private provider: ethers.JsonRpcProvider;
  private cache: RPCCache;
  
  constructor(
    rpcUrl: string,
    redisUrl: string,
    cacheConfig: CacheConfig
  ) {
    this.provider = new ethers.JsonRpcProvider(rpcUrl);
    this.cache = new RPCCache(redisUrl, cacheConfig);
  }
  
  async getBalance(address: string, blockTag: string = 'latest'): Promise<bigint> {
    const cached = await this.cache.get<bigint>('getBalance', [address, blockTag]);
    if (cached !== null) {
      return BigInt(cached);
    }
    
    const balance = await this.provider.getBalance(address, blockTag);
    await this.cache.set('getBalance', [address, blockTag], balance.toString());
    
    return balance;
  }
  
  async getBlock(blockNumber: number, includeTxs: boolean = false) {
    const cacheKey = `getBlock:${blockNumber}:${includeTxs}`;
    const cached = await this.cache.get<ethers.Block>(cacheKey, []);
    
    if (cached) {
      return cached;
    }
    
    const block = await this.provider.getBlock(blockNumber, includeTxs);
    await this.cache.set(cacheKey, [], block);
    
    return block;
  }
}

3.2 連接池管理

在高併發場景下,正確的連接池管理對於系統穩定性至關重要。

interface ConnectionPoolConfig {
  minConnections: number;
  maxConnections: number;
  acquireTimeout: number;
  idleTimeout: number;
  healthCheckInterval: number;
}

class RPCConnectionPool {
  private connections: ethers.JsonRpcProvider[] = [];
  private available: ethers.JsonRpcProvider[] = [];
  private inUse: Set<ethers.JsonRpcProvider> = new Set();
  private config: ConnectionPoolConfig;
  private healthCheckTimer: NodeJS.Timeout | null = null;
  
  constructor(
    private rpcUrls: string[],
    config: ConnectionPoolConfig
  ) {
    this.config = config;
    this.initialize();
  }
  
  private async initialize(): Promise<void> {
    // 初始連接池填充
    const initPromises = Array(this.config.minConnections)
      .fill(null)
      .map(async (_, index) => {
        const provider = new ethers.JsonRpcProvider(
          this.rpcUrls[index % this.rpcUrls.length]
        );
        await provider.ready;
        this.connections.push(provider);
        this.available.push(provider);
      });
    
    await Promise.all(initPromises);
    this.startHealthCheck();
  }
  
  async acquire(): Promise<ethers.JsonRpcProvider> {
    // 等待可用連接
    while (this.available.length === 0) {
      if (this.connections.length < this.config.maxConnections) {
        // 動態擴展連接池
        const provider = new ethers.JsonRpcProvider(
          this.rpcUrls[Math.floor(Math.random() * this.rpcUrls.length)]
        );
        await provider.ready;
        this.connections.push(provider);
        this.available.push(provider);
      } else {
        await new Promise(resolve => setTimeout(resolve, 100));
      }
    }
    
    const provider = this.available.pop()!;
    this.inUse.add(provider);
    return provider;
  }
  
  release(provider: ethers.JsonRpcProvider): void {
    if (!this.inUse.has(provider)) {
      console.warn('嘗試釋放未使用的連接');
      return;
    }
    
    this.inUse.delete(provider);
    this.available.push(provider);
  }
  
  private async healthCheck(): Promise<void> {
    const unhealthy: ethers.JsonRpcProvider[] = [];
    
    for (const provider of this.connections) {
      try {
        const blockNumber = await provider.getBlockNumber();
        if (blockNumber === 0) {
          unhealthy.push(provider);
        }
      } catch (error) {
        unhealthy.push(provider);
      }
    }
    
    // 替換故障節點
    for (const provider of unhealthy) {
      this.connections = this.connections.filter(p => p !== provider);
      this.available = this.available.filter(p => p !== provider);
      this.inUse.delete(provider);
      
      // 添加新節點
      const newProvider = new ethers.JsonRpcProvider(
        this.rpcUrls[Math.floor(Math.random() * this.rpcUrls.length)]
      );
      await newProvider.ready;
      this.connections.push(newProvider);
      this.available.push(newProvider);
    }
  }
  
  private startHealthCheck(): void {
    this.healthCheckTimer = setInterval(
      () => this.healthCheck(),
      this.config.healthCheckInterval
    );
  }
  
  destroy(): void {
    if (this.healthCheckTimer) {
      clearInterval(this.healthCheckTimer);
    }
    this.connections.forEach(p => p.destroy());
  }
}

3.3 速率限制與優先級佇列

企業級 RPC 服務需要精細的速率控制機制,以防止單一客戶端佔用過多資源。

interface RateLimitConfig {
  windowMs: number;
  maxRequests: number;
  priorityLevels: number[];
}

class RateLimitedRPCClient {
  private tokenBuckets: Map<string, { tokens: number; lastRefill: number }> = new Map();
  private requestQueues: Map<string, PriorityQueue<() => Promise<any>>> = new Map();
  private processing: boolean = false;
  
  constructor(
    private provider: ethers.JsonRpcProvider,
    private config: RateLimitConfig
  ) {}
  
  private async acquireToken(clientId: string): Promise<boolean> {
    const now = Date.now();
    const bucket = this.tokenBuckets.get(clientId);
    
    if (!bucket) {
      this.tokenBuckets.set(clientId, {
        tokens: this.config.maxRequests - 1,
        lastRefill: now
      });
      return true;
    }
    
    const elapsed = now - bucket.lastRefill;
    const tokensToAdd = Math.floor((elapsed / this.config.windowMs) * this.config.maxRequests);
    
    if (tokensToAdd > 0) {
      bucket.tokens = Math.min(
        this.config.maxRequests,
        bucket.tokens + tokensToAdd
      );
      bucket.lastRefill = now;
    }
    
    if (bucket.tokens > 0) {
      bucket.tokens--;
      return true;
    }
    
    return false;
  }
  
  async request<T>(
    method: string,
    params: any[],
    options: { clientId: string; priority?: number } = { clientId: 'default' }
  ): Promise<T> {
    const { clientId, priority = 5 } = options;
    
    // 嘗試獲取令牌
    const hasToken = await this.acquireToken(clientId);
    
    if (!hasToken) {
      // 加入優先級佇列等待
      return new Promise((resolve, reject) => {
        if (!this.requestQueues.has(clientId)) {
          this.requestQueues.set(clientId, new PriorityQueue<() => Promise<any>>());
        }
        
        const queue = this.requestQueues.get(clientId)!;
        queue.enqueue(() => this.executeRequest<T>(method, params), priority);
        
        // 設定超時
        setTimeout(() => {
          if (!queue.isEmpty()) {
            queue.dequeue();
            reject(new Error('請求超時'));
          }
        }, 30000);
      });
    }
    
    return this.executeRequest<T>(method, params);
  }
  
  private async executeRequest<T>(method: string, params: any[]): Promise<T> {
    try {
      const result = await this.provider.send(method, params);
      return result as T;
    } catch (error) {
      throw error;
    }
  }
}

// 優先級佇列實現
class PriorityQueue<T> {
  private items: { item: T; priority: number }[] = [];
  
  enqueue(item: T, priority: number): void {
    const newItem = { item, priority };
    let added = false;
    
    for (let i = 0; i < this.items.length; i++) {
      if (this.items[i].priority < priority) {
        this.items.splice(i, 0, newItem);
        added = true;
        break;
      }
    }
    
    if (!added) {
      this.items.push(newItem);
    }
  }
  
  dequeue(): T | undefined {
    return this.items.shift()?.item;
  }
  
  isEmpty(): boolean {
    return this.items.length === 0;
  }
}

第四章:2026 年 RPC 新特性與 EIP-7702 整合

4.1 Pectra 升級帶來的 RPC 變化

2026 年的 Pectra 升級為以太坊 RPC API 引入了多項重要更新。主要變化包括:

強化的事務處理能力

EIP-7702 的實施使得外部擁有帳戶(EOA)能夠臨時獲得智慧合約的功能。這為 RPC 交互帶來了新的可能性:

// EIP-7702:授權合約代碼執行
interface EIP7702Auth {
  contractAddress: string;
  signature: {
    v: number;
    r: string;
    s: string;
  };
  chainId: number;
  nonce: number;
  contractCode: string;
}

async function executeEIP7702Transaction(
  provider: ethers.JsonRpcProvider,
  auth: EIP7702Auth,
  targetCall: {
    to: string;
    value: bigint;
    data: string;
  }
): Promise<string> {
  // 構造 EIP-7702 授權交易
  const tx = {
    to: targetCall.to,
    value: targetCall.value,
    data: targetCall.data,
    authorizationList: [{
      contractAddress: auth.contractAddress,
      authorizer: ethers.zeroPadValue(auth.contractAddress, 32),
      nonce: auth.nonce,
      signature: ethers.joinSignature({
        v: auth.signature.v,
        r: auth.signature.r,
        s: auth.signature.s
      })
    }]
  };
  
  return await provider.send('eth_sendTransaction', [tx]);
}

新的 Blob 交易方法

隨著完整 Danksharding 的推進,Blob 交易相關的 RPC 方法變得更加重要:

// Blob 交易費用估算
async function estimateBlobFee(provider: ethers.JsonRpcProvider): Promise<{
  baseFee: bigint;
  blobFee: bigint;
  totalEstimate: bigint;
}> {
  const block = await provider.getBlock('pending');
  const blobGasPrice = await provider.send('eth_blobBaseFee', []);
  
  const baseFee = block?.baseFeePerGas || 0n;
  const blobFee = BigInt(blobGasPrice) * 131072n; // MAX_BLOBS_PER_BLOCK
  
  return {
    baseFee,
    blobFee,
    totalEstimate: baseFee + blobFee
  };
}

// 查詢 Blob 歷史價格
async function getBlobGasPriceHistory(
  provider: ethers.JsonRpcProvider,
  startBlock: number,
  endBlock: number
): Promise<{ block: number; price: bigint }[]> {
  const history: { block: number; price: bigint }[] = [];
  
  for (let block = startBlock; block <= endBlock; block += 1000) {
    const blockData = await provider.getBlock(block);
    const blobPrice = await provider.send('eth_getBlobGasPrice', [block]);
    
    history.push({
      block: blockData!.number,
      price: BigInt(blobPrice)
    });
  }
  
  return history;
}

4.2 批次請求最佳化

2026 年的 RPC 提供者普遍支援更高效的批次請求,這對於需要處理大量資料的應用程式至關重要:

class BatchRPCClient {
  private provider: ethers.JsonRpcProvider;
  private batchSize: number;
  private pendingRequests: Map<string, any> = new Map();
  private batchTimer: NodeJS.Timeout | null = null;
  
  constructor(provider: ethers.JsonRpcProvider, batchSize: number = 100) {
    this.provider = provider;
    this.batchSize = batchSize;
  }
  
  async batchRequest<T>(
    requests: { method: string; params: any[]; id: string }[]
  ): Promise<Map<string, T>> {
    const results = new Map<string, T>();
    
    // 分批處理
    for (let i = 0; i < requests.length; i += this.batchSize) {
      const batch = requests.slice(i, i + this.batchSize);
      const batchResults = await this.sendBatch(batch);
      
      batchResults.forEach((result, id) => {
        results.set(id, result as T);
      });
    }
    
    return results;
  }
  
  private async sendBatch(
    requests: { method: string; params: any[]; id: string }[]
  ): Promise<Map<string, any>> {
    const payload = requests.map(req => ({
      jsonrpc: '2.0',
      method: req.method,
      params: req.params,
      id: req.id
    }));
    
    const responses = await this.provider.send(payload);
    
    return new Map(
      responses.map((resp: any) => [resp.id, resp.result || resp.error])
    );
  }
}

// 使用範例:批量查詢多個錢包餘額
async function batchQueryBalances(
  client: BatchRPCClient,
  addresses: string[]
): Promise<Map<string, bigint>> {
  const requests = addresses.map((address, index) => ({
    method: 'eth_getBalance',
    params: [address, 'latest'],
    id: `balance_${index}`
  }));
  
  const results = await client.batchRequest<string>(requests);
  const balances = new Map<string, bigint>();
  
  results.forEach((balance, id) => {
    const address = addresses[parseInt(id.split('_')[1])];
    balances.set(address, BigInt(balance));
  });
  
  return balances;
}

第五章:安全性考量與最佳實踐

5.1 節點安全配置

RPC 節點的安全性配置是防止未經授權訪問的第一道防線:

interface NodeSecurityConfig {
  corsDomains: string[];
  corsMaxAge: number;
  rpcHosts: string[];
  authentication: {
    enabled: boolean;
    jwtSecret: string;
    jwtExpiry: string;
  };
  rateLimit: {
    enabled: boolean;
    requestsPerSecond: number;
    burstSize: number;
  };
  cors: {
    allowedOrigins: string[];
    allowedMethods: string[];
    allowedHeaders: string[];
  };
}

function generateGethSecurityConfig(config: NodeSecurityConfig): string {
  const args = [
    `--http.corsdomain=${config.corsDomains.join(',')}`,
    `--http.addr=0.0.0.0`,
    `--http.vhosts=*`,
    `--rpc.enabled=true`,
    `--rpcapi=eth,net,web3,debug,txpool`,
    `--rpccorsdomain=${config.corsMaxAge}`,
    `--auth.enabled=${config.authentication.enabled}`,
    `--auth.jwtsecret=${config.authentication.jwtSecret}`
  ];
  
  return args.join(' ');
}

class SecureRPCGateway {
  private provider: ethers.JsonRpcProvider;
  private jwtSecret: Buffer;
  private rateLimiter: RateLimiter;
  
  constructor(rpcUrl: string, config: NodeSecurityConfig) {
    this.provider = new ethers.JsonRpcProvider(rpcUrl);
    this.jwtSecret = Buffer.from(config.authentication.jwtSecret, 'hex');
    this.rateLimiter = new RateLimiter(config.rateLimit);
  }
  
  async authenticatedRequest<T>(
    method: string,
    params: any[],
    authToken: string
  ): Promise<T> {
    // 驗證 JWT token
    if (!this.verifyToken(authToken)) {
      throw new Error('認證失敗');
    }
    
    // 檢查速率限制
    await this.rateLimiter.checkLimit(authToken);
    
    return await this.provider.send(method, params);
  }
  
  private verifyToken(token: string): boolean {
    try {
      const jwt = JSONWebToken.verify(token, this.jwtSecret);
      return jwt !== null;
    } catch {
      return false;
    }
  }
}

5.2 資料驗證與類型安全

在使用 RPC 回傳的資料時,進行嚴格的類型檢查和驗證至關重要:

import { ethers } from 'ethers';

// 類型安全的 RPC 回應解析
class TypeSafeRPCParser {
  static parseAddress(value: unknown): string {
    if (typeof value !== 'string') {
      throw new Error(`預期 string,實際為 ${typeof value}`);
    }
    
    if (!ethers.isAddress(value)) {
      throw new Error(`無效的以太坊地址: ${value}`);
    }
    
    return value;
  }
  
  static parseBigInt(value: unknown): bigint {
    if (typeof value === 'bigint') {
      return value;
    }
    
    if (typeof value === 'string') {
      try {
        return BigInt(value);
      } catch {
        throw new Error(`無法解析 BigInt: ${value}`);
      }
    }
    
    if (typeof value === 'number') {
      return BigInt(value);
    }
    
    throw new Error(`預期 BigInt 相容類型,實際為 ${typeof value}`);
  }
  
  static parseTransaction(tx: unknown): ethers.TransactionResponse {
    if (!tx || typeof tx !== 'object') {
      throw new Error('交易資料格式無效');
    }
    
    const txObj = tx as Record<string, unknown>;
    
    // 驗證必要欄位
    const requiredFields = ['hash', 'from', 'to', 'value', 'gasLimit'];
    for (const field of requiredFields) {
      if (!(field in txObj)) {
        throw new Error(`缺少必要欄位: ${field}`);
      }
    }
    
    // 使用 ethers.js 進行完整解析(自帶驗證)
    return txObj as unknown as ethers.TransactionResponse;
  }
  
  static parseBlock(block: unknown): ethers.Block {
    if (!block || typeof block !== 'object') {
      throw new Error('區塊資料格式無效');
    }
    
    const blockObj = block as Record<string, unknown>;
    
    if (typeof blockObj.number !== 'number') {
      throw new Error('區塊編號格式無效');
    }
    
    return blockObj as unknown as ethers.Block;
  }
}

5.3 錯誤處理策略

完善的錯誤處理機制對於生產環境至關重要:

enum RPCErrorCode {
  PARSE_ERROR = -32700,
  INVALID_REQUEST = -32600,
  METHOD_NOT_FOUND = -32601,
  INVALID_PARAMS = -32602,
  INTERNAL_ERROR = -32603,
  SERVER_ERROR = -32000,
  NODE_CONNECTION_ERROR = -32001,
  TIMEOUT_ERROR = -32002,
  RATE_LIMIT_ERROR = -32003,
  INVALID_SIGNATURE = -32004
}

interface RPCError extends Error {
  code: RPCErrorCode;
  data?: any;
  method?: string;
  params?: any[];
  timestamp: number;
}

class EnhancedRPCError extends Error implements RPCError {
  code: RPCErrorCode;
  data?: any;
  method?: string;
  params?: any[];
  timestamp: number;
  
  constructor(
    code: RPCErrorCode,
    message: string,
    options?: {
      data?: any;
      method?: string;
      params?: any[];
    }
  ) {
    super(message);
    this.code = code;
    this.data = options?.data;
    this.method = options?.method;
    this.params = options?.params;
    this.timestamp = Date.now();
    this.name = 'EnhancedRPCError';
  }
  
  toJSON(): object {
    return {
      jsonrpc: '2.0',
      error: {
        code: this.code,
        message: this.message,
        data: this.data
      },
      id: null
    };
  }
}

class ResilientRPCClient {
  private providers: ethers.JsonRpcProvider[];
  private currentProviderIndex: number = 0;
  private retryDelays: number[] = [100, 500, 1000, 5000];
  
  constructor(rpcUrls: string[]) {
    this.providers = rpcUrls.map(url => new ethers.JsonRpcProvider(url));
  }
  
  async send<T>(method: string, params: any[]): Promise<T> {
    let lastError: Error | null = null;
    
    for (let attempt = 0; attempt < this.providers.length * 4; attempt++) {
      const provider = this.providers[this.currentProviderIndex];
      
      try {
        const result = await provider.send(method, params);
        return result as T;
      } catch (error) {
        lastError = error as Error;
        
        // 檢查是否為可重試的錯誤
        if (this.isRetryableError(error)) {
          await this.delay(this.retryDelays[Math.min(attempt, this.retryDelays.length - 1)]);
          this.rotateProvider();
          continue;
        }
        
        // 非可重試錯誤,直接拋出
        throw new EnhancedRPCError(
          RPCErrorCode.INTERNAL_ERROR,
          `RPC 請求失敗: ${lastError.message}`,
          { method, params, data: lastError }
        );
      }
    }
    
    throw new EnhancedRPCError(
      RPCErrorCode.SERVER_ERROR,
      '所有 RPC 提供者皆無法使用',
      { method, params, data: lastError }
    );
  }
  
  private rotateProvider(): void {
    this.currentProviderIndex = 
      (this.currentProviderIndex + 1) % this.providers.length;
  }
  
  private isRetryableError(error: any): boolean {
    // 網路錯誤、逾時、503 等都應重試
    const retryableCodes = [
      'ECONNREFUSED',
      'ETIMEDOUT',
      'ENOTFOUND',
      'NETWORK_ERROR',
      503,
      502,
      504
    ];
    
    return (
      retryableCodes.includes(error.code) ||
      (error.message && error.message.includes('timeout')) ||
      (error.status && retryableCodes.includes(error.status))
    );
  }
  
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

第六章:Python 實務整合

6.1 使用 web3.py 進行 RPC 整合

Python 生態系統中,web3.py 是最廣泛使用的以太坊 RPC 客戶端庫:

from web3 import Web3
from web3.eth import Eth
from web3.providers.rpc import HTTPProvider
from web3.exceptions import BlockNotFound, TransactionNotFound
from dataclasses import dataclass
from typing import Optional, List, Dict, Any
from enum import Enum
import asyncio
import aiohttp
from dataclasses import dataclass
from typing import Optional, List, Dict, Any, Union
from decimal import Decimal

class RPCProviderType(Enum):
    HTTP = "http"
    WEBSOCKET = "websocket"
    IPC = "ipc"

@dataclass
class RPCConfig:
    provider_type: RPCProviderType
    url: str
    request_kwargs: Optional[Dict[str, Any]] = None
    retry_attempts: int = 3
    retry_delay: float = 1.0
    timeout: float = 30.0

class EthereumRPCClient:
    def __init__(self, config: RPCConfig):
        self.config = config
        self._initialize_provider()
    
    def _initialize_provider(self):
        if self.config.provider_type == RPCProviderType.HTTP:
            self.provider = HTTPProvider(
                self.config.url,
                request_kwargs=self.config.request_kwargs or {}
            )
        elif self.config.provider_type == RPCProviderType.WEBSOCKET:
            from web3.providers.websocket import WebsocketProvider
            self.provider = WebsocketProvider(self.config.url)
        else:
            from web3.providers.ipc import IPCProvider
            self.provider = IPCProvider(self.config.url)
        
        self.w3 = Web3(self.provider)
    
    @property
    def is_connected(self) -> bool:
        return self.w3.is_connected()
    
    def get_block_number(self) -> int:
        return self.w3.eth.block_number
    
    def get_balance(self, address: str, block_tag: str = 'latest') -> int:
        address = self.w3.to_checksum_address(address)
        return self.w3.eth.get_balance(address, block_tag)
    
    def get_balance_ether(self, address: str, block_tag: str = 'latest') -> Decimal:
        balance_wei = self.get_balance(address, block_tag)
        return Decimal(balance_wei) / Decimal(10 ** 18)
    
    def get_transaction(self, tx_hash: str) -> Dict[str, Any]:
        tx_hash = bytes.fromhex(tx_hash.replace('0x', ''))
        return dict(self.w3.eth.get_transaction(tx_hash))
    
    def get_transaction_receipt(self, tx_hash: str) -> Dict[str, Any]:
        tx_hash = bytes.fromhex(tx_hash.replace('0x', ''))
        return dict(self.w3.eth.get_transaction_receipt(tx_hash))
    
    def get_block(self, block_identifier: Union[int, str]) -> Dict[str, Any]:
        if isinstance(block_identifier, str):
            if block_identifier not in ['latest', 'earliest', 'pending', 'safe', 'finalized']:
                raise ValueError(f"無效的區塊標識符: {block_identifier}")
        return dict(self.w3.eth.get_block(block_identifier))
    
    def call_contract_function(
        self,
        contract_address: str,
        function_name: str,
        abi: List[Dict],
        args: tuple = ()
    ) -> Any:
        contract_address = self.w3.to_checksum_address(contract_address)
        contract = self.w3.eth.contract(
            address=contract_address,
            abi=abi
        )
        func = getattr(contract.functions, function_name)
        return func(*args).call()
    
    def estimate_gas(self, transaction: Dict[str, Any]) -> int:
        return self.w3.eth.estimate_gas(transaction)
    
    def send_raw_transaction(self, signed_tx: bytes) -> bytes:
        return self.w3.eth.send_raw_transaction(signed_tx)
    
    def get_logs(
        self,
        address: Optional[str] = None,
        from_block: Union[int, str] = 'latest',
        to_block: Union[int, str] = 'latest',
        topics: Optional[List[str]] = None
    ) -> List[Dict[str, Any]]:
        filter_params = {
            'fromBlock': from_block,
            'toBlock': to_block
        }
        
        if address:
            filter_params['address'] = self.w3.to_checksum_address(address)
        
        if topics:
            filter_params['topics'] = topics
        
        logs = self.w3.eth.get_logs(filter_params)
        return [dict(log) for log in logs]

# 非同步版本的 RPC 客戶端
class AsyncEthereumRPCClient:
    def __init__(self, config: RPCConfig):
        self.config = config
        self.session: Optional[aiohttp.ClientSession] = None
        self.w3: Optional[Web3] = None
    
    async def __aenter__(self):
        self.session = aiohttp.ClientSession()
        
        if self.config.provider_type == RPCProviderType.HTTP:
            self.provider = HTTPProvider(
                self.config.url,
                request_kwargs={'session': self.session}
            )
        else:
            from web3.providers.websocket import WebsocketProvider
            self.provider = WebsocketProvider(self.config.url)
        
        self.w3 = Web3(self.provider)
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.session:
            await self.session.close()
    
    async def get_balance_async(self, address: str) -> int:
        address = self.w3.to_checksum_address(address)
        return await self.w3.eth.get_balance(address)
    
    async def batch_get_balances(self, addresses: List[str]) -> Dict[str, int]:
        """批量查詢多個地址的餘額"""
        results = {}
        
        # 構造 batch 請求
        batch_requests = []
        for i, address in enumerate(addresses):
            address = self.w3.to_checksum_address(address)
            batch_requests.append({
                'jsonrpc': '2.0',
                'method': 'eth_getBalance',
                'params': [address, 'latest'],
                'id': i
            })
        
        # 發送 batch 請求
        async with self.session.post(self.config.url, json=batch_requests) as response:
            results_list = await response.json()
        
        for i, result in enumerate(results_list):
            if 'result' in result:
                results[addresses[i]] = int(result['result'], 16)
        
        return results

6.2 Flask API 伺服器範例

將 RPC 功能封裝為 RESTful API:

from flask import Flask, request, jsonify
from functools import wraps
import time
from typing import Dict, Any

app = Flask(__name__)

# 初始化 RPC 客戶端
rpc_client = EthereumRPCClient(RPCConfig(
    provider_type=RPCProviderType.HTTP,
    url="https://eth.llamarpc.com",
    request_kwargs={'timeout': 30}
))

def rate_limit(max_requests: int = 60, window: int = 60):
    """簡單的速率限制裝飾器"""
    requests_log = {}
    
    def decorator(f):
        @wraps(f)
        def wrapped(*args, **kwargs):
            client_ip = request.remote_addr
            now = time.time()
            
            if client_ip not in requests_log:
                requests_log[client_ip] = []
            
            # 清理過期的請求記錄
            requests_log[client_ip] = [
                t for t in requests_log[client_ip]
                if now - t < window
            ]
            
            if len(requests_log[client_ip]) >= max_requests:
                return jsonify({
                    'error': '速率限制已觸發',
                    'retry_after': window
                }), 429
            
            requests_log[client_ip].append(now)
            return f(*args, **kwargs)
        
        return wrapped
    
    return decorator

@app.route('/api/v1/balance/<address>', methods=['GET'])
@rate_limit(max_requests=100)
def get_balance(address: str) -> Dict[str, Any]:
    """查詢錢包餘額"""
    try:
        balance_wei = rpc_client.get_balance(address)
        balance_eth = rpc_client.w3.from_wei(balance_wei, 'ether')
        
        return jsonify({
            'address': address,
            'balance_wei': str(balance_wei),
            'balance_eth': str(balance_eth),
            'block_number': rpc_client.get_block_number()
        })
    
    except ValueError as e:
        return jsonify({'error': '無效的地址格式'}), 400
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/v1/block/<block_id>', methods=['GET'])
@rate_limit(max_requests=50)
def get_block(block_id: str) -> Dict[str, Any]:
    """查詢區塊資訊"""
    try:
        if block_id == 'latest':
            block = rpc_client.get_block('latest')
        else:
            block = rpc_client.get_block(int(block_id))
        
        # 轉換 Decimal 和 bytes 為可序列化格式
        result = {}
        for key, value in block.items():
            if hasattr(value, '__str__'):
                result[key] = str(value)
            elif isinstance(value, bytes):
                result[key] = value.hex()
            elif isinstance(value, list):
                result[key] = [
                    str(v) if hasattr(v, '__str__') else v
                    for v in value
                ]
            else:
                result[key] = value
        
        return jsonify(result)
    
    except (ValueError, BlockNotFound) as e:
        return jsonify({'error': str(e)}), 404
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/v1/transaction/<tx_hash>', methods=['GET'])
@rate_limit(max_requests=100)
def get_transaction(tx_hash: str) -> Dict[str, Any]:
    """查詢交易詳情"""
    try:
        tx = rpc_client.get_transaction(tx_hash)
        
        result = {}
        for key, value in tx.items():
            if hasattr(value, '__str__'):
                result[key] = str(value)
            elif isinstance(value, bytes):
                result[key] = value.hex()
            else:
                result[key] = value
        
        return jsonify(result)
    
    except TransactionNotFound:
        return jsonify({'error': '交易不存在'}), 404
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/v1/logs', methods=['POST'])
@rate_limit(max_requests=20)
def get_logs() -> Dict[str, Any]:
    """查詢事件日誌"""
    try:
        data = request.get_json()
        
        filter_params = {
            'from_block': data.get('from_block', 'latest'),
            'to_block': data.get('to_block', 'latest')
        }
        
        if 'address' in data:
            filter_params['address'] = data['address']
        
        if 'topics' in data:
            filter_params['topics'] = data['topics']
        
        logs = rpc_client.get_logs(**filter_params)
        
        return jsonify({
            'logs': logs,
            'count': len(logs)
        })
    
    except Exception as e:
        return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=False)

第七章:效能監控與調優

7.1 RPC 指標收集

建立完善的監控系統對於維護 RPC 服務至關重要:

interface RPCMetrics {
  totalRequests: number;
  successfulRequests: number;
  failedRequests: number;
  averageLatency: number;
  p50Latency: number;
  p95Latency: number;
  p99Latency: number;
  errorsByCode: Map<number, number>;
}

class RPCMetricsCollector {
  private metrics: {
    requests: { latency: number; success: boolean; errorCode?: number }[];
    startTime: number;
  } = {
    requests: [],
    startTime: Date.now()
  };
  
  private maxSamples = 10000;
  
  recordRequest(latencyMs: number, success: boolean, errorCode?: number): void {
    this.metrics.requests.push({
      latency: latencyMs,
      success,
      errorCode
    });
    
    // 限制樣本數量以控制記憶體使用
    if (this.metrics.requests.length > this.maxSamples) {
      this.metrics.requests.shift();
    }
  }
  
  getMetrics(): RPCMetrics {
    const latencies = this.metrics.requests
      .map(r => r.latency)
      .sort((a, b) => a - b);
    
    const successfulRequests = this.metrics.requests.filter(r => r.success).length;
    const errorsByCode = new Map<number, number>();
    
    this.metrics.requests.forEach(r => {
      if (!r.success && r.errorCode) {
        errorsByCode.set(
          r.errorCode,
          (errorsByCode.get(r.errorCode) || 0) + 1
        );
      }
    });
    
    const sum = latencies.reduce((a, b) => a + b, 0);
    const avg = latencies.length > 0 ? sum / latencies.length : 0;
    
    return {
      totalRequests: this.metrics.requests.length,
      successfulRequests,
      failedRequests: this.metrics.requests.length - successfulRequests,
      averageLatency: avg,
      p50Latency: this.percentile(latencies, 50),
      p95Latency: this.percentile(latencies, 95),
      p99Latency: this.percentile(latencies, 99),
      errorsByCode
    };
  }
  
  private percentile(sortedArray: number[], p: number): number {
    if (sortedArray.length === 0) return 0;
    
    const index = Math.ceil((p / 100) * sortedArray.length) - 1;
    return sortedArray[Math.max(0, index)];
  }
  
  reset(): void {
    this.metrics.requests = [];
    this.metrics.startTime = Date.now();
  }
}

// 自動化監控整合
class MonitoringRPCWrapper {
  private metrics: RPCMetricsCollector;
  
  constructor(
    private provider: ethers.JsonRpcProvider,
    private alertThreshold: { latency: number; errorRate: number }
  ) {
    this.metrics = new RPCMetricsCollector();
  }
  
  async send(method: string, params: any[]): Promise<any> {
    const start = performance.now();
    
    try {
      const result = await this.provider.send(method, params);
      const latency = performance.now() - start;
      
      this.metrics.recordRequest(latency, true);
      this.checkAlerts();
      
      return result;
    } catch (error: any) {
      const latency = performance.now() - start;
      const errorCode = this.parseErrorCode(error);
      
      this.metrics.recordRequest(latency, false, errorCode);
      this.checkAlerts();
      
      throw error;
    }
  }
  
  private parseErrorCode(error: any): number {
    if (error.code) return error.code;
    if (error.response?.error?.code) return error.response.error.code;
    return -1;
  }
  
  private checkAlerts(): void {
    const metrics = this.metrics.getMetrics();
    
    if (metrics.p95Latency > this.alertThreshold.latency) {
      console.warn(`延遲警報: P95=${metrics.p95Latency.toFixed(2)}ms`);
    }
    
    const errorRate = metrics.failedRequests / metrics.totalRequests;
    if (errorRate > this.alertThreshold.errorRate) {
      console.warn(`錯誤率警報: ${(errorRate * 100).toFixed(2)}%`);
    }
  }
}

7.2 自動故障轉移

class FailoverRPCManager {
  private providers: {
    url: string;
    provider: ethers.JsonRpcProvider;
    health: { healthy: boolean; lastCheck: number; consecutiveFailures: number };
  }[];
  
  private currentIndex: number = 0;
  private readonly maxFailures: number = 3;
  private readonly healthCheckInterval: number = 30000;
  
  constructor(rpcUrls: string[]) {
    this.providers = rpcUrls.map(url => ({
      url,
      provider: new ethers.JsonRpcProvider(url),
      health: {
        healthy: true,
        lastCheck: Date.now(),
        consecutiveFailures: 0
      }
    }));
    
    this.startHealthChecks();
  }
  
  async getBestProvider(): Promise<ethers.JsonRpcProvider> {
    // 優先使用當前健康且最近檢查過的節點
    const healthy = this.providers
      .filter(p => p.health.healthy)
      .sort((a, b) => b.health.lastCheck - a.health.lastCheck);
    
    if (healthy.length > 0) {
      return healthy[0].provider;
    }
    
    // 如果所有節點都不健康,返回當前選擇的節點
    return this.providers[this.currentIndex].provider;
  }
  
  async executeWithFailover<T>(
    operation: (provider: ethers.JsonRpcProvider) => Promise<T>
  ): Promise<T> {
    const errors: Error[] = [];
    
    for (let attempt = 0; attempt < this.providers.length; attempt++) {
      const provider = await this.getBestProvider();
      const providerInfo = this.providers.find(p => p.provider === provider);
      
      try {
        const result = await operation(provider);
        if (providerInfo) {
          providerInfo.health.consecutiveFailures = 0;
        }
        return result;
      } catch (error) {
        errors.push(error as Error);
        
        if (providerInfo) {
          providerInfo.health.consecutiveFailures++;
          
          if (providerInfo.health.consecutiveFailures >= this.maxFailures) {
            providerInfo.health.healthy = false;
            console.warn(`節點 ${providerInfo.url} 標記為不健康`);
          }
        }
        
        // 切換到下一個節點
        this.currentIndex = (this.currentIndex + 1) % this.providers.length;
      }
    }
    
    // 所有節點都失敗
    throw new Error(`所有 RPC 提供者皆失敗: ${errors.map(e => e.message).join('; ')}`);
  }
  
  private startHealthChecks(): void {
    setInterval(async () => {
      for (const providerInfo of this.providers) {
        try {
          await providerInfo.provider.getBlockNumber();
          
          if (!providerInfo.health.healthy) {
            providerInfo.health.healthy = true;
            providerInfo.health.consecutiveFailures = 0;
            console.log(`節點 ${providerInfo.url} 恢復健康`);
          }
        } catch {
          // 健康檢查失敗不計入連續失敗計數
        }
        
        providerInfo.health.lastCheck = Date.now();
      }
    }, this.healthCheckInterval);
  }
}

結語

以太坊 RPC API 是區塊鏈應用開發的基石。隨著以太坊網路持續演進,特別是 2026 年 Pectra 升級和 EIP-7702 的實施,RPC 的使用場景變得更加豐富多元。本文涵蓋了從基礎概念到企業級架構設計的完整知識體系,包括:

  1. 基礎理論:JSON-RPC 協議原理、執行層與共識層 RPC 的區別、傳輸層選擇
  2. 核心方法:區塊查詢、交易處理、狀態讀取、交易廣播等關鍵操作
  3. 企業架構:多節點部署、快取策略、連接池管理、速率限制
  4. 新特性整合:EIP-7702、Blob 交易、批次請求優化
  5. 安全性:節點安全配置、資料驗證、錯誤處理
  6. Python 實作:web3.py 整合、Flask API 開發
  7. 監控運維:指標收集、故障轉移、性能優化

掌握這些技術將幫助開發者構建高效、可靠、安全的以太坊應用程式。隨著以太坊生態系統的不斷發展,建議持續關注官方文檔更新和新興最佳實踐。


參考資源

免責聲明

本網站內容僅供教育與資訊目的,不構成任何投資建議或推薦。在進行任何加密貨幣相關操作前,請自行研究並諮詢專業人士意見。所有投資均有風險,請謹慎評估您的風險承受能力。

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!