Skip to content

Task 1-2: RPC Redundancy với Fallback Providers

Phase: 1 Priority: Medium Module: solana Depends on: Không có Reference: docs/bountyhunter-blockchain-p2/details/feature-solana-integration/SPEC.md

Background

Hiện tại SolanaUtil sử dụng một RPC endpoint duy nhất từ config — đây là single point of failure. Khi RPC provider downtime hoặc rate limit, toàn bộ Solana operations sẽ fail. Cần implement fallback RPC strategy: thử primary trước, nếu fail chuyển sang secondary, v.v. Pattern này phổ biến trong production Solana services và cần thiết cho high availability.

Tasks

Note: Connection từ @solana/web3.js phải được tạo với RPC URL cụ thể. Không thể thay đổi RPC của connection hiện tại — cần tạo connection mới cho mỗi RPC. SolanaUtil nên maintain array of Connection và logic failover. Import Connection, Commitment từ @solana/web3.js.

  • [ ] Cập nhật SolanaUtil constructor để nhận array RPC URLs:
    export class SolanaUtil {
      private readonly connections: Connection[];
      private currentConnectionIndex = 0;
      private readonly logger = new Logger(SolanaUtil.name);
    
      constructor(private readonly configService: ConfigService) {
        const rpcUrls = [
          configService.get<string>('SOLANA_RPC_URL'),
          configService.get<string>('SOLANA_RPC_FALLBACK_1'),
          configService.get<string>('SOLANA_RPC_FALLBACK_2'),
        ].filter(Boolean) as string[];
    
        this.connections = rpcUrls.map((url) => new Connection(url, 'confirmed'));
        this.logger.log(`[SOLANA] Initialized with ${this.connections.length} RPC endpoints`);
      }
    
      get connection(): Connection {
        return this.connections[this.currentConnectionIndex];
      }
    }
    
  • [ ] Implement failoverToNext() method:
    private failoverToNext(): boolean {
      if (this.currentConnectionIndex < this.connections.length - 1) {
        this.currentConnectionIndex++;
        this.logger.warn(
          `[SOLANA] RPC failover → switching to endpoint ${this.currentConnectionIndex + 1}/${this.connections.length}`
        );
        return true;
      }
      return false; // No more fallbacks
    }
    
  • [ ] Tích hợp failover vào sendWithRetry (task-1-1):
    // Khi lỗi là RPC error → thử failover
    if (isRpcError(error) && this.failoverToNext()) {
      continue; // retry với connection mới
    }
    
  • [ ] Thêm biến môi trường vào .env.example:
    SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
    SOLANA_RPC_FALLBACK_1=https://rpc.helius.xyz/?api-key=YOUR_KEY
    SOLANA_RPC_FALLBACK_2=https://solana-mainnet.g.alchemy.com/v2/YOUR_KEY
    
  • [ ] Reset currentConnectionIndex sau khoảng thời gian (e.g., 5 phút) để thử lại primary:
    setTimeout(() => {
      this.currentConnectionIndex = 0;
      this.logger.log('[SOLANA] Reset to primary RPC endpoint');
    }, 5 * 60 * 1000);
    

Verification / Acceptance Criteria

  • [ ] Khi primary RPC trả lỗi connection → tự động switch sang SOLANA_RPC_FALLBACK_1
  • [ ] Khi cả primary và fallback 1 fail → switch sang SOLANA_RPC_FALLBACK_2
  • [ ] Sau 5 phút → reset về primary và thử lại
  • [ ] Khi chỉ có SOLANA_RPC_URL (không có fallback) → hoạt động bình thường (single endpoint)
  • [ ] Log rõ khi failover xảy ra với endpoint index
  • [ ] TypeScript compile không có lỗi

Files to Modify

  • src/solana/utils/solana.util.ts
  • .env.example