Relaying Sequence

Supported Features
- Compatible with all major EVM chains (Ethereum, Base, Optimism, Arbitrum, Polygon, and more)
- Request-driven design for configurable, on-demand relaying
- Transaction failure retry support
- Re-orgs
- Out-of-gas
- Inadequate gas price
- Tx network propagation fails to reach leader
- Invalid by the network, but valid by the submitting node
- Transaction Tracking API
- Remote signing support
- Concurrent packet intake and processing
- Configurable packet delivery latency via batching
- Ability to blacklist addresses (ex: OFAC)
- Transaction cost tracking
Getting Started
Prerequisites
- Go 1.24+
- Docker and Docker Compose
- A running proof API service
- RPC endpoints for the chains you want to relay between
Local Development
- Start Postgres and run migrations:
- Create a local config file (see Configuration Reference below).
- Create a local keys file (see Local Signing below).
- Run the relayer:
- gRPC API server on the address configured in
relayer_api.address - Prometheus metrics server on the configured address
- Relay dispatcher polling for new transfers
CLI Flags
| Flag | Default | Description |
|---|---|---|
--config | ./config/local/config.yml | Path to relayer config file |
--ibcv2-relaying | true | Enable/disable the relay dispatcher |
Database Migrations
Database migrations must be run before starting the relayer. The relayer expects the database schema to already exist. Local Development:Design


API Interface
The relayer serves a gRPC server which clients use to specify what packets to relay and track packet relaying progress.Observability
| Type | Name | Description |
|---|---|---|
| Metric | Relayer api request count | Paginated by method and response code |
| Metric | Relayer api request latency | Paginated by method |
| Metric | Transfer count | Paginated by source, destination chain, and transfer state |
| Metric | Relayer gas balance | Paginated by chain and gas token |
| Metric | Relayer gas balance state | A gauge where each value represents a gas balance state. 0 = ok, 1 = warning, 2 = critical. Paginated by chain |
| Metric | External request count | Paginated by endpoint, method and response code |
| Metric | External request latency | Paginated by endpoint and method |
| Metric | Transactions submitted counter | Paginated by node response success status and chain |
| Metric | Transaction retry counter | Paginated by source and destination chain |
| Metric | Transactions confirmed counter | Paginated by execution success and chain |
| Metric | Transaction gas cost counter | Paginated by chain |
| Metric | Relay latency | Time between send tx and ack/timeout tx. Paginated by source and destination chain |
| Metric | Detected client update required counter | Paginated by chain |
| Metric | Client updated counter | Paginated by chain |
| Metric | Excessive relay latency counter | Incremented anytime a transfer is pending longer than a configured threshold. Paginated by source and destination chain |
| Alert | Excessive relay latency | Should alert whenever the excessive relay latency counter increases |
| Alert | Excessive gas usage | Should alert whenever the gas cost counter increases faster than some threshold |
| Alert | Low gas balance | Should alert whenever the relayer gas balance state metric is in the warning or critical state |
Configuration Reference
The relayer is configured via a YAML file. Below is the complete configuration schema with all available options.Full Example
Section Reference
postgres
| Field | Type | Description |
|---|---|---|
hostname | string | Postgres host |
port | string | Postgres port |
database | string | Database name |
POSTGRES_USER and POSTGRES_PASSWORD (default: relayer/relayer).
metrics
| Field | Type | Description |
|---|---|---|
prometheus_address | string | Address to serve Prometheus metrics (e.g. 0.0.0.0:8888) |
relayer_api
| Field | Type | Description |
|---|---|---|
address | string | Address for the gRPC API server to listen on (e.g. 0.0.0.0:9000) |
ibcv2_proof_api
Connection to the proof API service that generates relay transactions.
| Field | Type | Description |
|---|---|---|
grpc_address | string | gRPC address of the proof API |
grpc_tls_enabled | bool | Enable TLS for the proof API connection |
signing
Signing configuration. The mode is inferred from which fields are set:
- If
grpc_addressis set → remote signing (ignoreskeys_path) - Else if
keys_pathis set → local signing from key file - Else → fatal error at startup
| Field | Type | Description |
|---|---|---|
keys_path | string | Path to local signing keys JSON file |
grpc_address | string | gRPC address of the remote signer service. If set, takes precedence over keys_path |
cosmos_wallet_key | string | Wallet ID for Cosmos chain signing (remote signer only) |
evm_wallet_key | string | Wallet ID for EVM chain signing (remote signer only) |
svm_wallet_key | string | Wallet ID for Solana chain signing (remote signer only) |
coingecko (optional)
Used for tracking transaction gas costs in USD. If omitted, gas cost tracking is disabled.
| Field | Type | Description |
|---|---|---|
base_url | string | Coingecko API base URL |
api_key | string | API key |
requests_per_minute | int | Rate limit |
cache_refresh_interval | duration | How often to refresh cached prices |
chains.<chain_key>
Each entry under chains defines a chain the relayer can interact with.
| Field | Type | Description |
|---|---|---|
chain_name | string | Human-readable chain name. Used primarily in metrics. |
chain_id | string | Chain identifier (numeric for EVM, string for Cosmos) |
type | string | cosmos, evm, or svm |
environment | string | mainnet or testnet |
gas_token_symbol | string | Gas token ticker symbol |
gas_token_coingecko_id | string | Coingecko ID for gas cost tracking (optional) |
gas_token_decimals | uint8 | Decimal places for the gas token |
supported_bridges | []string | List of bridge types (currently only ibcv2) |
chains.<chain_key>.cosmos
Required when type: cosmos.
| Field | Type | Description |
|---|---|---|
gas_price | float64 | Gas price for fee estimation. Mutually exclusive with ibcv2_tx_fee_amount |
ibcv2_tx_fee_denom | string | Fee denom for ibcv2 txs (required if ibcv2_tx_fee_amount is set) |
ibcv2_tx_fee_amount | uint64 | Fixed fee amount for ibcv2 txs. Mutually exclusive with gas_price |
rpc | string | Tendermint RPC endpoint |
rpc_basic_auth_var | string | Environment variable name containing basic auth credentials for RPC |
grpc | string | gRPC endpoint |
grpc_tls_enabled | bool | Enable TLS for gRPC |
address_prefix | string | Bech32 address prefix (e.g. cosmos, osmo) |
chains.<chain_key>.evm
Required when type: evm.
| Field | Type | Description |
|---|---|---|
rpc | string | Ethereum JSON-RPC endpoint |
rpc_basic_auth_var | string | Environment variable name containing basic auth credentials for RPC |
contracts.ics_26_router_address | string | ICS26 Router contract address |
contracts.ics_20_transfer_address | string | ICS20 Transfer contract address |
gas_fee_cap_multiplier | float64 | Multiplier applied to the estimated gas fee cap (optional) |
gas_tip_cap_multiplier | float64 | Multiplier applied to the estimated gas tip cap (optional) |
chains.<chain_key>.ibcv2
IBC v2 relay configuration for this chain.
| Field | Type | Description |
|---|---|---|
counterparty_chains | map[string]string | Maps client IDs on this chain to their counterparty chain IDs. Only connections listed here will be relayed |
finality_offset | uint64 | Number of blocks to wait after a tx before considering it finalized. If omitted, uses the chain’s native finality |
recv_batch_size | int | Max packets to accumulate before flushing a recv batch |
recv_batch_timeout | duration | Max time to wait for recv packets to accumulate before flushing |
recv_batch_concurrency | int | Max concurrent recv batches being processed |
ack_batch_size | int | Max packets to accumulate before flushing an ack batch |
ack_batch_timeout | duration | Max time to wait for ack packets to accumulate before flushing |
ack_batch_concurrency | int | Max concurrent ack batches being processed |
timeout_batch_size | int | Max packets to accumulate before flushing a timeout batch |
timeout_batch_timeout | duration | Max time to wait for timeout packets to accumulate before flushing |
timeout_batch_concurrency | int | Max concurrent timeout batches being processed |
should_relay_success_acks | bool | Whether to relay acknowledgements for successful packet deliveries |
should_relay_error_acks | bool | Whether to relay acknowledgements for failed packet deliveries |
chains.<chain_key>.signer_gas_alert_thresholds
| Field | Type | Description |
|---|---|---|
ibcv2.warning_threshold | string | Gas balance (in smallest denom) at which the metric reports warning state |
ibcv2.critical_threshold | string | Gas balance (in smallest denom) at which the metric reports critical state |
Signing
The relayer supports two signing modes, configured via thesigning block in the YAML config. The mode is inferred from which fields are populated:
- If
grpc_addressis set → remote signing (ignoreskeys_path) - Else if
keys_pathis set → local signing from key file - Else → fatal error at startup
Local Signing
Setsigning.keys_path to point to a JSON file containing private keys. The format is a map of chain IDs to key objects:
Remote Signing
For production deployments, the relayer can delegate signing to an external gRPC service. This keeps private keys isolated from the relayer process.SERVICE_ACCOUNT_TOKEN environment variable as a bearer token in gRPC metadata for authenticating requests to the signing service.
The remote signer must implement the following gRPC service:
Sign RPC accepts transaction payloads for EVM, Cosmos, and Solana chains and returns the appropriate signature format:
- EVM: Accepts serialized tx bytes + chain ID, returns
(r, s, v)signature components - Cosmos: Accepts sign doc bytes, returns a raw signature
- Solana: Accepts a base64-encoded transaction, returns a raw signature
Upcoming Features
- Solana support
Unsupported Features
- Charging end users fees to relay IBC transactions
- Relaying IBC v1 packets