A Python-based Oasis ROFL (Runtime OFf-chain Logic) oracle that fetches block headers from source chains and submits them to the ROFLAdapter contract on Oasis Sapphire for cross-chain bridge verification.
This oracle listens for BlockHeaderRequested events from a source chain
contract and responds by fetching the requested block headers and submitting
them to the Oasis Sapphire network through the ROFL runtime. It's designed to
work as part of a Hashi-based cross-chain bridge system.
- Source Chain: Listens for events on any EVM-compatible blockchain
- Target Chain: Submits block headers to Oasis Sapphire via ROFL
- ROFL Runtime: Uses Oasis confidential compute for secure oracle operations
- Event-Driven: Processes
BlockHeaderRequestedevents in real-time
- Docker and Docker Compose
- Access to a ROFL-enabled Oasis Network environment
- Source chain RPC endpoint
- Deployed contracts:
BlockHeaderRequesteron source chainROFLAdapteron Oasis Sapphire
The oracle is configured through environment variables defined in compose.yaml:
The oracle supports three modes: event_listener, push, and watcher. Some environment variables are required for all modes, while others are specific to a mode.
| Variable | Description | Default | Required |
|---|---|---|---|
PYTHONUNBUFFERED |
Disable Python output buffering for immediate logs | 1 |
No |
SOURCE_RPC_URL |
RPC endpoint for the source chain | https://ethereum.publicnode.com |
No |
TARGET_RPC_URL |
RPC endpoint for the target chain | https://testnet.sapphire.oasis.io |
No |
ROFL_ADAPTER_ADDRESS |
Address of the ROFLAdapter contract on Oasis Sapphire | - | Yes |
REQUEST_TIMEOUT |
HTTP request timeout (seconds) | 30 |
No |
RETRY_COUNT |
Number of retry attempts for operations | 3 |
No |
ORACLE_MODE |
Operating mode: event_listener, push, or watcher |
event_listener |
No |
LOG_LEVEL |
Logging level: DEBUG, INFO, WARNING, ERROR, CRITICAL | INFO |
No |
JSON_LOGS |
Enable JSON-formatted structured logging | false |
No |
| Variable | Description | Default | Required |
|---|---|---|---|
SOURCE_CONTRACT_ADDRESS |
Address of the BlockHeaderRequester contract on source chain | - | Yes |
POLLING_INTERVAL |
Seconds between event checks | 12 |
No |
LOOKBACK_BLOCKS |
Number of blocks to look back on startup | 100 |
No |
| Variable | Description | Default | Required |
|---|---|---|---|
PUSH_INTERVAL |
Seconds between block pushes | 60 |
No |
PUSH_BATCH_SIZE |
Max blocks to push per iteration | 20 |
No |
| Variable | Description | Default | Required |
|---|---|---|---|
WATCH_ADDRESSES |
Comma-separated list of addresses to watch | - | Yes |
SCAN_INTERVAL |
Seconds between scanning for interactions | 60 |
No |
WATCHER_BATCH_SIZE |
Max blocks to scan per iteration | 50 |
No |
LOOKBACK_BLOCKS |
Number of blocks to look back on startup | 100 |
No |
ENABLE_INTERNAL_TX_DETECTION |
Enable internal transaction detection | false |
No |
Internal Transaction Detection:
When ENABLE_INTERNAL_TX_DETECTION=true, the watcher will also detect interactions
that occur via internal transactions (contract-to-contract calls). This feature:
- Requires: Archive node with
debug_traceTransactionAPI support - Performance: Significantly slower due to tracing overhead (trace each transaction)
- Use case: Critical when watched addresses primarily interact via smart contracts
- Recommended: Only enable if you have access to archive nodes and need comprehensive tracking
Without this feature, only direct (external) transactions to/from watched addresses are detected.
| Variable | Description | Default | Required |
|---|---|---|---|
LOCAL_PRIVATE_KEY |
Private key for local testing mode | - | Yes (Local Mode) |
Note:
- All addresses must be valid EVM addresses (checksummed).
LOCAL_PRIVATE_KEYis only required for local mode/testing.- If a required variable is missing for the selected mode, the oracle will fail to start with a clear error.
PYTHONUNBUFFERED=1: Essential for real-time log visibility in containerized environments. Without this, log output may be buffered and not appear immediately, making the oracle appear "stuck" when it's actually running normally.LOCAL_PRIVATE_KEY: Required only when running in local mode for testing without ROFL utilities. Should be a hex-encoded private key.ENABLE_INTERNAL_TX_DETECTION: When enabled in watcher mode, usesdebug_traceTransactionto detect internal contract calls. This requires:- Archive node access (full nodes won't work)
- Debug API enabled on the RPC endpoint
- May require premium RPC provider plans
- Significantly increases processing time (10-100x slower)
- Only use if absolutely necessary for your use case
Create a .env file or set environment variables:
# Required for all modes
export ROFL_ADAPTER_ADDRESS=0xYourROFLAdapterAddress
export SOURCE_RPC_URL=https://your-source-chain-rpc.com
export ORACLE_MODE=event_listener
# Required for event_listener mode
export SOURCE_CONTRACT_ADDRESS=0xYourBlockHeaderRequesterAddress
# Optional: configure other settings
export POLLING_INTERVAL=12
export LOOKBACK_BLOCKS=100docker-compose up --buildThe oracle will display:
- Initialization progress
- ROFL connection status
- Block range being monitored
- Event processing status
- Periodic heartbeat messages
For testing and development without ROFL infrastructure, the oracle supports a local mode that simulates transaction submissions and skips ROFL utilities.
- No ROFL Dependencies: Skips ROFL utility initialization and socket connections
- Transaction Simulation: Logs transaction details instead of actual submission
- Event Listening: Full WebSocket and polling event listening functionality
- Local Private Key: Uses a local private key for contract interaction testing
Use the dedicated local testing Docker Compose configuration:
# Run with local testing configuration
docker compose -f compose.local.yaml up --buildIn addition to the standard variables, local mode requires:
# Required for local mode - add this to your .env file
LOCAL_PRIVATE_KEY=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef- Initialization: Connects to ROFL runtime and source chain
- Event Monitoring: Polls for
BlockHeaderRequestedevents - Block Fetching: Retrieves requested block headers from source chain
- Header Submission: Submits headers to Sapphire via ROFL
- Continuous Operation: Runs in an infinite loop with configurable intervals
The oracle includes built-in health check endpoints for container orchestration and monitoring:
/health- Overall system health with component status/health/live- Liveness probe (is the service running)/health/ready- Readiness probe (is the service ready to handle work)
Health endpoints are exposed on port 8080 by default.
# Check overall health
curl http://localhost:8080/health
# Liveness probe (for Kubernetes/Docker)
curl http://localhost:8080/health/live
# Readiness probe
curl http://localhost:8080/health/readyThe oracle includes production-grade error handling:
- Exponential Backoff: Automatic retry with exponential backoff for transient failures
- Circuit Breakers: Prevents cascading failures when RPCs are down
- Retry Logic: Configurable retry attempts (via
RETRY_COUNT) for all critical operations - Graceful Degradation: Continues operating even with degraded connectivity
Enable JSON-formatted structured logging for production environments:
export JSON_LOGS=trueThis outputs logs in JSON format for easier parsing by log aggregation systems (ELK, Splunk, DataDog, etc.).
- Python 3.10+
- oasis-sapphire-py
- web3.py
- httpx
- cbor2
- aiohttp
# Install dependencies
make sync
# Run tests
make test
# Run tests with coverage
make test-cov
# Format code
make format
# Lint code
make lint
# Fix linting issues
make lint-fix
# Run all checks (format, lint, test)
make pre-commit
# Run the application (via Docker Compose)
docker compose -f compose.local.yaml up --buildThis oracle is designed to work with:
- Hashi cross-chain message verification system
- Oasis ROFL confidential compute runtime
- EVM-compatible source chains