# Technical Design & Architecture This document's purpose is to visually represent the Rewards Eligibility Oracle codebase, as a more approachable alternative to reading through the codebase directly. ## End-to-End Oracle Flow The Rewards Eligibility Oracle operates as a daily scheduled service that evaluates indexer performance and updates on-chain rewards eligibility via function calls to the RewardsEligibilityOracle contract. The diagram below illustrates the complete execution flow from scheduler trigger through data processing to blockchain submission and error handling. The Oracle is designed to be resilient to transient network issues and RPC provider failures. It uses a multi-layered approach involving internal retries, provider rotation, and a circuit breaker to prevent costly infinite restart loops that needlessly burn through BigQuery requests. ```mermaid --- title: Rewards Eligibility Oracle - End-to-End Flow --- %%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#fee2e2', 'primaryTextColor':'#7f1d1d', 'primaryBorderColor':'#ef4444', 'lineColor':'#6b7280'}}}%% graph TB %% Docker Container - Contains all oracle logic subgraph DOCKER["Docker Container"] Scheduler["Python Scheduler"] Oracle["Rewards Eligibility Oracle"] subgraph CIRCUIT_BREAKER["Circuit Breaker Logic"] CB["Circuit Breaker"] CBCheck{"Has there been more
than 3 failures in the
last 60 minutes?"} end Scheduler -.->|"Phase 1: Schedule daily run"| Oracle %% Data Pipeline subgraph PIPELINE["Data Pipeline"] CacheCheck{"Do we have recent cached
BigQuery results available?
(< 30 min old)"} subgraph BIGQUERY["BigQuery Analysis"] FetchData["Fetch Indexer Performance Data
over last 28 days
(from BigQuery)"] SQLQuery["- Daily query metrics
- Days online calculation
- Subgraph coverage"] end subgraph PROCESSING["Eligibility Processing"] ApplyCriteria["Apply Criteria e.g.
5+ days online
Latency < 5000ms
Blocks behind < 50000
1+ subgraph served"] FilterData["Filter Eligible
vs Ineligible"] GenArtifacts["Generate CSV Artifacts:
- eligible_indexers.csv
- ineligible_indexers.csv
- full_metrics.csv"] end end %% Blockchain Layer subgraph BLOCKCHAIN["Blockchain Submission"] Batch["Consume series of Eligible
Indexers from CSV.
Batch indexer addresses
into groups of 125 indexers."] subgraph RPC["RPC Failover System"] TryRPC["Try establish connection
with RPC provider"] RPCError["Rotate to next RPC provider"] end BuildTx["Build Transaction:
- Estimate gas
- Get nonce
- Sign with key"] SubmitTx["Submit Batch to Contract
call function:
renewIndexerEligibility()"] WaitReceipt["Wait for Receipt
30s timeout"] MoreBatches{"More
Batches?"} end %% Monitoring subgraph MONITOR["Monitoring & Notifications"] SlackSuccess["Slack Success:
- Eligible count
- Execution time
- Transaction links"] SlackFailCircuitBreaker["Stop container sys.exit(0)
Container will not restart
Manual Intervention needed
Send notification to team
slack channel for debugging"] SlackFailRPC["Stop container sys.exit(1)
Container will restart
Send notification to slack"] SlackRotate["Send slack notification"] end end %% External Systems - Define after Docker subgraph RPCProviders["Pool of 4 RPC providers
(External Infrastructure)"] BQ["Google BigQuery
Indexer Performance Data"] subgraph FailureLogStorage["Data Storage
(mounted volume)"] CBLog["Failure log"] end subgraph HistoricalDataStorage["Data Storage
(mounted volume)"] HistoricalData["Historical archive of
eligible and ineligible
indexers by date
YYYY-MM-DD"] end END_NO_RESTART["FAILURE
Container Stopped
No Restart
Manual Intervention Required"] END_WITH_RESTART["FAILURE
Container Stopped
Restart Container
Will retry entire loop again"] SUCCESS["SUCCESS
Wait for next
scheduled trigger"] %% Main Flow - Start with Docker container to anchor it left Oracle -->|"Phase 1.1: Check if oracle
should run"| CB CB -->|"Phase 1.2: Read log"| CBLog CBLog -->|"Phase 1.3: Return log"| CB CB -->|"Phase 1.4: Provides failure
timestamps (if they exist)"| CBCheck CBCheck -->|"Phase 2:
(Regular Path)
No"| CacheCheck CacheCheck -->|"Phase 2.1: Check for
recent cached data"| HistoricalData HistoricalData -->|"Phase 2.2: Return recent eligible indexers
from eligible_indexers.csv
(if they exist)"| CacheCheck CBCheck -.->|"Phase 2:
(Alternative Path)
Yes"| SlackFailCircuitBreaker SlackFailCircuitBreaker -.-> END_NO_RESTART CacheCheck -->|"Phase 3:
(Alternative Path)
Yes"| Batch CacheCheck -->|"Phase 3:
(Regular Path)
No"| FetchData FetchData -->|"Phase 3.1: Query data
from BigQuery"| BQ BQ -->|"Phase 3.2: Returns metrics"| SQLQuery SQLQuery -->|"Phase 3.3: Process results"| ApplyCriteria ApplyCriteria --> FilterData FilterData -->|"Phase 3.4: Generate CSV's"| GenArtifacts GenArtifacts -->|"Phase 3.5: Save data"| HistoricalData GenArtifacts --> Batch Batch -->|"Phase 4.1: For each batch"| TryRPC TryRPC -->|"Phase 4.2: Connect"| RPCProviders RPCProviders -->|"Phase 4.3:
(Regular Path)
RPC connection established"| BuildTx RPCProviders -.->|"Phase 4.3:
(Alternative Path)
RPC connection failed
Multiple connection attempts
Not possible to connect"| RPCError RPCError -.->|"Notify"| SlackRotate RPCError -->|"All exhausted"| SlackFailRPC SlackFailRPC --> END_WITH_RESTART RPCError -->|"Connection successful"| BuildTx BuildTx --> SubmitTx SubmitTx --> WaitReceipt WaitReceipt -->|"Phase 4.4: Batch confirmed"| MoreBatches MoreBatches -->|"Yes
Back to phase 4 loop
Process next batch"| Batch MoreBatches -->|"Phase 5: No
All complete"| SlackSuccess SlackSuccess --> SUCCESS %% Styling classDef schedulerStyle fill:#fee2e2,stroke:#ef4444,stroke-width:3px,color:#7f1d1d classDef oracleStyle fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#92400e classDef dataStyle fill:#f0fdf4,stroke:#16a34a,stroke-width:2px,color:#14532d classDef processingStyle fill:#e0e7ff,stroke:#6366f1,stroke-width:2px,color:#312e81 classDef blockchainStyle fill:#fee2e2,stroke:#ef4444,stroke-width:2px,color:#7f1d1d classDef monitorStyle fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#92400e classDef infraStyle fill:#f3f4f6,stroke:#6b7280,stroke-width:2px,color:#374151 classDef contractStyle fill:#dbeafe,stroke:#2563eb,stroke-width:3px,color:#1e3a8a classDef decisionStyle fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#92400e classDef endStyle fill:#7f1d1d,stroke:#991b1b,stroke-width:3px,color:#fee2e2 classDef endStyleOrange fill:#ea580c,stroke:#c2410c,stroke-width:3px,color:#ffedd5 classDef successStyle fill:#14532d,stroke:#166534,stroke-width:3px,color:#f0fdf4 class Scheduler schedulerStyle class Oracle,CB oracleStyle class FetchData,SQLQuery,BQ dataStyle class ApplyCriteria,FilterData,GenArtifacts processingStyle class Batch,TryRPC,BuildTx,SubmitTx,WaitReceipt,Rotate,RPCError blockchainStyle class SlackSuccess,SlackFailCircuitBreaker,SlackFailRPC,SlackRotate monitorStyle class RPCProviders,HistoricalData,CBLog infraStyle class Contract contractStyle class CacheCheck,MoreBatches,CBCheck decisionStyle class END_NO_RESTART endStyle class END_WITH_RESTART endStyleOrange class SUCCESS successStyle style DOCKER fill:#dbeafe,stroke:#2563eb,stroke-width:3px,color:#1e3a8a style CIRCUIT_BREAKER fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#92400e style PIPELINE fill:#f0fdf4,stroke:#16a34a,stroke-width:2px,color:#14532d style BIGQUERY fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d style PROCESSING fill:#e0e7ff,stroke:#6366f1,stroke-width:2px,color:#312e81 style BLOCKCHAIN fill:#fee2e2,stroke:#ef4444,stroke-width:2px,color:#7f1d1d style RPC fill:#fecaca,stroke:#ef4444,stroke-width:2px,color:#7f1d1d style MONITOR fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#92400e style FailureLogStorage fill:#f3f4f6,stroke:#6b7280,stroke-width:2px,color:#374151 style HistoricalDataStorage fill:#f3f4f6,stroke:#6b7280,stroke-width:2px,color:#374151 ``` --- ## RPC Provider Failover and Circuit Breaker Logic The application is designed to be resilient to transient network issues and RPC provider failures. It uses a multi-layered approach involving internal retries, provider rotation, and an application-level circuit breaker to prevent catastrophic failures and infinite restart loops. The following diagram illustrates the sequence of events when all RPC providers fail, leading to a single recorded failure by the circuit breaker. ```mermaid sequenceDiagram # Setup column titles participant main_oracle as rewards_eligibility_oracle.py participant blockchain_client as blockchain_client.py participant circuit_breaker as circuit_breaker.py participant slack_notifier as slack_notifier.py # Attempt function call main_oracle->>blockchain_client: batch_allow_indexers_issuance_eligibility() # Describe failure loop inside the blockchain_client module activate blockchain_client loop For each provider in pool # Attempt RPC call blockchain_client->>blockchain_client: _execute_rpc_call() with next provider note right of blockchain_client: Fails after 3 attempts # Log failure and rotate blockchain_client-->>blockchain_client: raises ConnectionError note right of blockchain_client: Catches error, rotates to next provider # Send rotation notification blockchain_client->>slack_notifier: send_info_notification() note right of slack_notifier: RPC provider rotation alert end note right of blockchain_client: All providers exhausted # Raise error back to main_oracle oracle and exit blockchain_client module blockchain_client-->>main_oracle: raises Final ConnectionError deactivate blockchain_client # Take note of the failure in the circuit breaker, which can break the restart loop if triggered enough times in a short duration main_oracle->>circuit_breaker: record_failure() # Notify of the RPC failure in slack main_oracle->>slack_notifier: send_failure_notification() # Document restart process note right of main_oracle: sys.exit(1) triggers Docker restart note right of main_oracle: Circuit breaker uses sys.exit(0) to prevent restart ```