A TypeScript-based blockchain bridge relayer that monitors and processes bridge events between Ethereum and Via networks. The relayer loads bridge initiation events from a subgraph, processes them, and maintains a comprehensive database of all bridge operations.
- Subgraph Integration: Loads bridge initiation events (
MessageSent) from L1 and L2 subgraphs - Automatic Transaction Processing: Processes bridge transactions automatically with configurable workers
- Database Integration: Stores all transactions in PostgreSQL with TypeORM
- Reliable RPC Connections: Uses ethers.js with failover provider support for stable blockchain connectivity
- Comprehensive Logging: Detailed logging with Winston for monitoring and debugging
- Event-driven Architecture: React to bridge events with proper status tracking
MessageSent: Bridge initiation event (loaded from L1 subgraph)MessageWithdrawalExecuted: Bridge finalization event (loaded from L1 subgraph)
MessageSent: Bridge initiation event (loaded from L2 subgraph)DepositExecuted: Bridge finalization event (loaded from L2 subgraph)
- Node.js 18+
- PostgreSQL database with subgraph data
- Access to Ethereum and Via RPC endpoints
- Bridge contract addresses
- Relayer private key for transaction signing
- Clone the repository:
git clone <repository-url>
cd via-relayer- Install dependencies:
npm install- Set up environment variables:
cp .env.example .env
# Edit .env with your configuration- Create the database:
npm run create-db- Run database migrations:
npm run migration:runCopy .env.example to .env and configure the following variables:
ETH_URL: Ethereum RPC URL (e.g.,http://localhost:8545orhttps://mainnet.infura.io/v3/YOUR-PROJECT-ID)VIA_URL: Via RPC URL (e.g.,http://localhost:8546)ETH_FALLBACK_URLS: Comma-separated fallback Ethereum RPC URLsVIA_FALLBACK_URLS: Comma-separated fallback Via RPC URLs
ETHEREUM_BRIDGE_ADDRESS: Address of the Ethereum bridge contractVIA_BRIDGE_ADDRESS: Address of the Via bridge contract
RELAYER_PRIVATE_KEY: Private key of the relayer wallet for signing transactionsWORKER_POLLING_INTERVAL: Polling interval for worker threads (in milliseconds)ETH_WAIT_BLOCK_CONFIRMATIONS: Block confirmations for Ethereum eventsVIA_WAIT_BLOCK_CONFIRMATIONS: Block confirmations for Via eventsTRANSACTION_BATCH_SIZE: Number of events to process per batch
DATABASE_URL: Complete PostgreSQL connection string (alternative to individual settings)DATABASE_HOST: Database host (default: localhost)DATABASE_PORT: Database port (default: 5432)DATABASE_USER: Database username (default: postgres)DATABASE_PASSWORD: Database password (default: postgres)DATABASE_NAME: Database name
L1_GRAPH_SCHEMA: PostgreSQL schema for L1 subgraph data (e.g.,sgd3)L2_GRAPH_SCHEMA: PostgreSQL schema for L2 subgraph data (e.g.,sgd2)
npm run devnpm run build
npm start# Create database
npm run create-db
# Generate migration
npm run migration:generate
# Run migrations
npm run migration:run
# Revert last migration
npm run migration:revert# Watch for TypeScript changes
npm run watch
# Development with auto-restart
npm run dev:watchThe Via Bridge Relayer facilitates cross-chain token transfers between Ethereum and Via networks through a systematic monitoring and processing flow:
Subgraph indexes MessageSent events from bridge contracts
↓
Relayer queries subgraph database for new events
↓
Events are filtered by block number (with confirmations)
↓
Events are processed in order
BridgeInitiatedHandler picks up MessageSent events
↓
Checks if transaction already exists in database
↓
Calls receiveMessage() on destination bridge contract
↓
Creates transaction record with PENDING status
BridgeFinalizeHandler queries for executed events
↓
Matches executed events to pending transactions
↓
Updates transaction status to FINALIZED
NEW → PENDING → FINALIZED
↓
FAILED
- NEW: Event detected but not yet processed
- PENDING: Transaction sent to destination chain, awaiting confirmation
- FINALIZED: Transaction confirmed on destination chain
- FAILED: Transaction failed during processing
┌─────────────────┐ ┌─────────────────┐
│ L1 Subgraph │ │ L2 Subgraph │
│ (Ethereum) │ │ (Via) │
└─────────────────┘ └─────────────────┘
│ │
│ MessageSent │ MessageSent
│ events │ events
▼ ▼
┌─────────────────────────────────────────┐
│ BridgeInitiatedHandler │
│ ┌─────────────────────────────────────┐│
│ │ - Query events from subgraph ││
│ │ - Check for duplicates ││
│ │ - Send to destination chain ││
│ │ - Record in transactions table ││
│ └─────────────────────────────────────┘│
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Database Layer │
│ ┌─────────────────────────────────────┐│
│ │ Transactions Table ││
│ │ - bridgeInitiatedTransactionHash ││
│ │ - finalizedTransactionHash ││
│ │ - status, origin, payload ││
│ │ - originBlockNumber, blockNumber ││
│ └─────────────────────────────────────┘│
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ BridgeFinalizeHandler │
│ ┌─────────────────────────────────────┐│
│ │ - Query executed events ││
│ │ - Match to pending transactions ││
│ │ - Update status to FINALIZED ││
│ └─────────────────────────────────────┘│
└─────────────────────────────────────────┘
- Subgraph-based Event Detection: Events are loaded from indexed subgraph data for reliability
- Duplicate Prevention: Transactions are checked by hash before processing
- Block Confirmation: Events are only processed after sufficient block confirmations
- Status Tracking: Complete visibility into transaction processing status
- Cross-Chain Coordination: Ensures both chains are synchronized during bridge operations
- Transaction Service (
transaction/): Manages worker threads for processing bridge transactions - Transaction Processor (
transaction.processor.ts): Orchestrates handler creation and batch processing - Handlers (
transaction/handlers/):BridgeInitiatedHandler: Processes new bridge initiation eventsBridgeSendFinalizeHandler: Handles sending finalization transactionsBridgeFinalizeHandler: Monitors and updates finalized transactions
- Database Layer (
database/): TypeORM-based data persistence - Repositories:
TransactionRepository: Main transaction recordsL1MessageSentRepository: L1 subgraph MessageSent eventsL2MessageSentRepository: L2 subgraph MessageSent eventsDepositExecutedRepository: L2 DepositExecuted eventsMessageWithdrawalExecutedRepository: L1 MessageWithdrawalExecuted events
The relayer maintains the following key table:
transactions
id: Primary keyorigin: Bridge origin (Ethereum=0, Via=1)status: Transaction status (New, Pending, Finalized, Failed)bridgeInitiatedTransactionHash: Original transaction hashfinalizedTransactionHash: Relayer's finalization transaction hashblockNumber: Finalization block numberoriginBlockNumber: Original event block numberpayload: Message payloadeventType: Type of bridge eventsubgraphId: ID from subgraph
The relayer uses a worker-based architecture for processing transactions:
- Transaction Workers: Process bridge transactions for each status type
- Parallel Processing: Separate workers for Ethereum and Via origins
- Configurable Polling: Adjustable polling interval for worker threads
| Variable | Description | Default | Required |
|---|---|---|---|
ETH_URL |
Ethereum RPC URL | http://host.docker.internal:8545 |
Yes |
VIA_URL |
Via RPC URL | http://host.docker.internal:8546 |
Yes |
ETHEREUM_BRIDGE_ADDRESS |
Ethereum bridge contract address | - | Yes |
VIA_BRIDGE_ADDRESS |
Via bridge contract address | - | Yes |
RELAYER_PRIVATE_KEY |
Relayer wallet private key | - | Yes |
WORKER_POLLING_INTERVAL |
Worker polling interval (ms) | 5000 |
No |
ETH_WAIT_BLOCK_CONFIRMATIONS |
Ethereum block confirmations | 6 |
No |
VIA_WAIT_BLOCK_CONFIRMATIONS |
Via block confirmations | 6 |
No |
TRANSACTION_BATCH_SIZE |
Events per batch | 25 |
No |
L1_GRAPH_SCHEMA |
L1 subgraph schema | sgd3 |
No |
L2_GRAPH_SCHEMA |
L2 subgraph schema | sgd2 |
No |
DATABASE_URL |
PostgreSQL connection string | - | No |
DATABASE_HOST |
Database host | localhost |
No |
DATABASE_PORT |
Database port | 5432 |
No |
DATABASE_USER |
Database username | postgres |
No |
DATABASE_PASSWORD |
Database password | postgres |
No |
DATABASE_NAME |
Database name | via_relayer |
No |
NODE_ENV |
Environment mode | development |
No |
LOG_LEVEL |
Logging level | info |
No |
ENABLE_FILE_LOGGING |
Enable file logging | true |
No |
src/
├── common/ # Common utilities and workers
├── contracts/ # Contract ABIs and interfaces
├── database/ # Database configuration and repositories
├── entities/ # TypeORM entities
├── migrations/ # Database migrations
├── transaction/ # Transaction processing logic
│ └── handlers/ # Event handlers
├── transformers/ # Data transformers
├── types/ # TypeScript type definitions
└── utils/ # Utility functions
npm testThe relayer uses Winston for logging with the following levels:
error: Error messageswarn: Warning messagesinfo: Information messagesdebug: Debug messages
Logs are output to console by default, with optional file logging in production.
- Set
NODE_ENV=production - Configure database connection pooling
- Set up log rotation for file logging
- Monitor RPC provider connection health
- Set up alerts for bridge event processing failures
- Configure appropriate worker polling intervals for your network conditions
-
RPC Connection Errors
- Check network connectivity to blockchain endpoints
- Verify RPC URLs are correct and accessible
- Check firewall settings
- Monitor rate limits on RPC providers
-
Database Connection Issues
- Verify PostgreSQL is running
- Check database credentials
- Ensure database and subgraph schemas exist
-
Missing Events
- Check subgraph indexing status
- Verify subgraph schema names in configuration
- Check block confirmation settings
-
Transaction Failures
- Verify relayer wallet has sufficient gas tokens
- Check relayer private key is valid
- Monitor for contract-level restrictions
The relayer provides comprehensive logging for monitoring:
- RPC provider connection status
- Event processing statistics
- Database operation status
- Transaction processing metrics
- Worker thread activity and health
Run with Docker Compose:
docker-compose up -dThis starts:
- PostgreSQL database
- Via Relayer application
MIT License
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
For support and questions, please check the documentation or create an issue in the repository.