OpenZeppelin Relayer
Overview
This software is in alpha stage. Use in production environments at your own risk. |
OpenZeppelin Relayer is a service that provides infrastructure to relay transactions to the EVM & Non-EVM networks. It is designed to be used as a backend for dApps that need to interact with these networks.
Features
-
Multi-Chain Support: Interact with multiple blockchain networks, including Solana and EVM-based chains.
-
Transaction Relaying: Submit transactions to supported blockchain networks efficiently.
-
Transaction Signing: Securely sign transactions using configurable key management.
-
Transaction Fee Estimation: Estimate transaction fees for better cost management.
-
Solana Gasless Transactions: Support for gasless transactions on Solana, enabling users to interact without transaction fees.
-
Transaction Nonce Management: Handle nonce management to ensure transaction order.
-
Transaction Status Monitoring: Track the status of submitted transactions.
-
SDK Integration: Easily interact with the relayer through our companion JavaScript/TypeScript SDK.
-
Extensible Architecture: Easily add support for new blockchain networks.
-
Configurable Network Policies: Define and enforce network-specific policies for transaction processing.
-
Metrics and Observability: Monitor application performance using Prometheus and Grafana.
-
Docker Support: Deploy the relayer using Docker for both development and production environments.
Supported Networks
-
Solana
-
EVM (Basic support for Ethereum and other EVM-compatible chains):
-
Ethereum (Mainnet, Sepolia, Holesky)
-
Optimism (Mainnet, Sepolia)
-
Worldchain (Sepolia)
-
For information about our development plans and upcoming features, see Project Roadmap. |
To get started immediately, see Quickstart. |
Technical Overview
Project Structure
The project follows a standard Rust project layout:
openzeppelin-relayer/
├── src/
│ ├── api/ # Route and controllers logic
│ ├── bootstrap/ # Service initialization logic
│ ├── config/ # Configuration logic
│ ├── constants/ # Constant values used in the system
│ ├── domain/ # Domain logic
│ ├── jobs/ # Asynchronous processing logic (queueing)
│ ├── logging/ # Logs File rotation logic
│ ├── metrics/ # Metrics logic
│ ├── models/ # Data structures and types
│ ├── repositories/ # Configuration storage
│ ├── services/ # Services logic
│ └── utils/ # Helper functions
│
├── config/ # Configuration files
├── tests/ # Integration tests
├── docs/ # Documentation
├── scripts/ # Utility scripts
├── examples/ # Configuration examples
├── helpers/ # Rust helper scripts
└── ... other root files (Cargo.toml, README.md, etc.)
For detailed information about each directory and its contents, see Project Structure Details.
Getting Started
Prerequisites
-
Rust 2021 edition
-
Docker (optional, for containerized deployment)
Ready-to-Use Example Configurations For quick setup with various configurations, check the examples directory in our GitHub repository:
Each example includes a README with step-by-step instructions and Docker Compose configuration. |
Install Locally
-
Clone the repository:
git clone https://github.com/openzeppelin/openzeppelin-relayer cd openzeppelin-relayer
-
Verify you have sodium libs installed. If not, follow these instructions:
-
Install a stable libsodium version from here.
-
Follow the steps in the libsodium installation guide.
-
-
Install dependencies:
cargo build
Running the Relayer
Option 1: Run Locally
cargo run
Before executing the command, ensure that the .env and config.json files are configured as detailed in the Configuration References section.
|
Option 2: Run with Docker
The Relayer can be run as either a development or production container using the corresponding Dockerfile (Dockerfile.development
or Dockerfile.production
).
Step 1: Configure Environment
-
Edit
.env
at the root of the repository to adjust environment variables -
The appropriate .env file will be included during image build
Configuration References
Most configuration files should live under ./config
, including the signer configurations, under ./config/keys
.
Please ensure appropriate access permissions on all configuration files (for ./config/keys/*
, we recommend 0500
.
The configuration system consists of two main components:
Both files must be properly configured before starting the application. Changes to either file require restarting the container to take effect. For quick setup examples with pre-configured files, see the examples directory in our GitHub repository. |
Environment configuration (.env)
This defines some base configurations for the Relayer application:
Copy the example environment file and update values according to your needs
cp .env.example .env
This table lists the environment variables and their default values.
Environment Variable | Default Value | Accepted Values | Description |
---|---|---|---|
|
|
|
Log level. |
|
|
|
Relative path of directory where config files reside |
|
|
|
File Name of the configuration file. |
|
|
|
Rate limit for the API in requests per second. |
|
|
|
Rate limit burst size. |
|
`` |
|
API key to use for authentication to the relayer server. Minimum length 32 characters. |
|
`` |
|
Signing key to use for webhook notifications. Minimum length 32 characters. |
|
|
|
Write logs either to console or to file. |
|
|
|
Directory to persist log files on host. |
|
|
|
Size after which logs needs to be rolled. |
|
|
|
Enable metrics server for external tools to scrape metrics. |
|
|
|
Port to use for metrics server. |
|
|
|
Redis connection URL for the relayer. |
|
|
|
Connection timeout for Redis in milliseconds. |
|
|
|
Enable or disable Swagger UI for API documentation. |
|
`` |
|
Passphrase for the keystore file used for signing transactions. |
Environment configuration example
.env
file config example:
RUST_LOG=DEBUG
CONFIG_DIR=./config
CONFIG_FILE_NAME=config.json
WEBHOOK_SIGNING_KEY=e1d42480-6f74-4d0b-85f4-b7f0bb690fae
API_KEY=5eefd216-0e44-4ca7-b421-2925f90d30d5
RATE_LIMIT_RPS=100
RATE_LIMIT_BURST_SIZE=300
METRICS_ENABLED=true
METRICS_PORT=8081
REDIS_URL=redis://localhost:6379
REDIS_CONNECTION_TIMEOUT_MS=10000
ENABLE_SWAGGER=false
KEYSTORE_PASSPHRASE=your_keystore_passphrase
Main configuration file (config.json)
This file can exist in any directory, but the default location is ./config/config.json
.
Copy the example config file and update values according to your needs
cp config/config.example.json config/config.json
There are 3 important sections in this file:
-
Signers: Defines transaction signing methods.
-
Notifications: Sets up status alerts
-
Relayers: Configures networks, notifications channels, policies & singers.
1. Signers
-
signers
array, which must contain, at least, one valid signer configuration:
Example:
"signers": [
{
"id": "my_id",
"type": "local",
"config": {
"path": "config/keys/local-signer.json",
"passphrase": {
"type": "env",
"value": "KEYSTORE_PASSPHRASE"
}
}
}
]
Supported signer types:
-
test
: temporary key only for testing -
local
: keystore file signer -
vault
: vault secret signer -
vault_cloud
: hosted vault secret signer -
vault_transit
: vault transit signer
Available configuration fields
Field | Type | Description |
---|---|---|
id |
String |
Unique id for the signer |
type |
String |
Type of signer (see |
config |
Map |
signer type related config |
local
type config:
Field | Type | Description |
---|---|---|
path |
String |
path to the signer json file. Should be under the |
passphrase.type |
String |
Type of passphrase ( |
passphrase.value |
String |
Passphrase value, env variable name, … |
vault
type config:
Field | Type | Description |
---|---|---|
address |
String |
Specifies the Vault API endpoint. |
role_id.type |
String |
Type of value source ( |
role_id.value |
String |
The Vault AppRole role identifier value, or the environment variable name where the AppRole role identifier is stored. |
secret_id.type |
String |
Type of value source ( |
secret_id.value |
String |
The Vault AppRole role secret value, or the environment variable name where the AppRole secret value is stored. |
key_name |
String |
The name of the cryptographic key within Vault’s Secret engine that is used for signing operations. |
mount_point |
String |
The mount point for the Secrets engine in Vault. Defaults to |
vault_cloud
type config:
Field | Type | Description |
---|---|---|
client_id |
String |
The client identifier used to authenticate with Vault Cloud. |
client_secret.type |
String |
Type of value source ( |
client_secret.value |
String |
The Vault secret value, or the environment variable name where the secret value is stored. |
org_id |
String |
The organization ID for your Vault Cloud account. |
project_id |
String |
The project ID that uniquely identifies your Vault Cloud project. |
app_name |
String |
The name of the application integrating with Vault Cloud. |
key_name |
String |
The name of the cryptographic key used for signing or encryption operations in Vault Cloud. |
vault_transit
type config:
Field | Type | Description |
---|---|---|
address |
String |
Specifies the Vault API endpoint. |
role_id.type |
String |
Type of value source ( |
role_id.value |
String |
The Vault AppRole role identifier value, or the environment variable name where the AppRole role identifier is stored. |
secret_id.type |
String |
Type of value source ( |
secret_id.value |
String |
The Vault AppRole role secret value, or the environment variable name where the AppRole secret value is stored. |
key_name |
String |
The name of the cryptographic key within Vault’s Transit engine that is used for signing operations. |
mount_point |
String |
The mount point for the Transit secrets engine in Vault. Defaults to |
namespace |
String |
The Vault namespace for API calls. This is used only in Vault Enterprise environments. Optional. |
pubkey |
String |
Public key of the cryptographic key within Vault’s Transit engine that is used for signing operations |
2. Notifications
-
notifications
array, which should contain, at least, one valid configuration:
"notifications": [
{
"id": "notification-test",
"type": "webhook",
"url": "https://webhook.site/f95cf78d-742d-4b21-88b7-d683e6fd147b",
"signing_key": {
"type": "env",
"value": "WEBHOOK_SIGNING_KEY"
}
}
]
Available configuration fields
Field | Type | Description |
---|---|---|
id |
String |
Unique id for the notification |
type |
String |
Type of notification (only |
url |
String |
Notification URL |
signing_key.type |
String |
Type of key used in signing the notification ( |
signing_key.value |
String |
Signing key value, env variable name, … |
3. Relayers
-
relayers
array, containing at least one valid relayer configuration:
"relayers": [
{
"id": "solana-testnet",
"name": "Solana Testnet",
"paused": false,
"notification_id": "notification-test",
"signer_id": "local-signer",
"network_type": "solana",
"network": "testnet",
"policies": {
"allowed_programs": [
"11111111111111111111111111111111",
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"BPFLoaderUpgradeab1e11111111111111111111111"
]
}
},
]
Available configuration fields
Field | Type | Description |
---|---|---|
id |
String |
Unique id for the relayer |
name |
String |
Human readable name for the relayer |
paused |
Boolean |
Whether or not the relayer is paused ( |
notification_id |
String |
ID of a configured notification object |
signer_id |
String |
ID of a configured signer |
network_type |
String |
Type of network the relayer will connect to ( |
network |
String |
Network the relayer will connect to. Please refer to |
custom_rpc_urls |
list |
Optional custom RPC URLs for the network. If provided, this will be used instead of the public RPC URLs. This is useful for using your own RPC node or a paid service provider. The first url of the list is going to be used as the default |
policies |
list |
Overrides default policies. Please refer to the |
Supported networks
Network type | Network | Description |
---|---|---|
solana |
mainnet-beta |
Solana’s mainnet |
solana |
devnet |
Solana’s devnet |
solana |
testnet |
Solana’s testnet |
evm |
mainnet |
Ethereum mainnet |
evm |
sepolia |
Ethereum testnet |
evm |
holesky |
Ethereum testnet |
Policies
Network type | Policy | Type | Description |
---|---|---|---|
solana, evm |
min_balance |
unsigned 128 |
Minimum balance (in lamports or wei) required for the relayer to operate. Optional. |
solana |
fee_payment_strategy |
enum(user,relayer) |
Specifies who pays the fee. "user" (default) means the sender pays; "relayer" means the relayer pays. For "user", RPC methods add an instruction to transfer SPL tokens (calculated from the current SOL price plus a configurable margin) from the user to the relayer, ensuring fees are sustainably covered in tokens rather than SOL. |
solana |
fee_margin_percentage |
f32 |
Additional margin percentage added to estimated transaction fees to account for price fluctuations. For example, a value of 10 will add 10% to estimated fees. Optional. |
solana |
max_allowed_fee_lamports |
unsigned 64 |
Maximum allowed fee (in lamports) for a transaction. Optional. |
solana |
allowed_tokens |
Vector<AllowedToken> |
List of allowed tokens. Only these tokens are supported if provided. Optional. |
solana |
allowed_programs |
Vector<String> |
List of allowed programs by their identifiers. Only these programs are supported if provided. |
solana |
allowed_accounts |
Vector<String> |
List of allowed accounts by their public keys. The relayer will only operate with these accounts if provided. |
solana |
disallowed_accounts |
Vector<String> |
List of disallowed accounts by their public keys. These accounts will be explicitly blocked. |
solana |
max_tx_data_size |
unsigned 16 |
Maximum transaction size. Optional. |
solana |
max_signatures |
unsigned 8 |
Maximum supported signatures. Optional. |
evm |
gas_price_cap |
unsigned 128 |
Specify a maximum gas price for every transaction sent with the Relayer. When enabled, any transaction exceeding the cap will have its gasPrice or maxFeePerGas overwritten. (Optional) |
evm |
whitelist_receivers |
Vector<String> |
A list of authorized contracts for each transaction sent using the Relayer. Transactions will be rejected if the destination address is not on the list. (Optional) |
Config file full example
Full config/config.json
example with evm and solana relayers definitions using keystore signer:
{
"relayers": [
{
"id": "sepolia-example",
"name": "Sepolia Example",
"network": "sepolia",
"paused": false,
"notification_id": "notification-example",
"signer_id": "local-signer",
"network_type": "evm",
"custom_rpc_urls": ["https://your-private-rpc-url.example.com"],
"policies": {
"gas_price_cap": 30000000000000,
"eip1559_pricing": true
}
},
{
"id": "solana-example",
"name": "Solana Example",
"network": "devnet",
"paused": false,
"notification_id": "notification-example",
"signer_id": "local-signer",
"network_type": "solana",
"custom_rpc_urls": ["https://your-private-solana-rpc.example.com"],
"policies": {
"fee_payment_strategy": "user",
"min_balance": 0,
"allowed_tokens": [
{
"mint": "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr",
"max_allowed_fee": 100000000
},
{
"mint": "So11111111111111111111111111111111111111112"
}
]
}
}
],
"notifications": [
{
"id": "notification-example",
"type": "webhook",
"url": "https://webhook.site/1384d4d9-21b1-40a0-bcd1-d3f3b66be955",
"signing_key": {
"type": "env",
"value": "WEBHOOK_SIGNING_KEY"
}
}
],
"signers": [
{
"id": "local-signer",
"type": "local",
"config": {
"path": "config/keys/local-signer.json",
"passphrase": {
"type": "env",
"value": "KEYSTORE_PASSPHRASE"
}
}
}
]
}
```
RPC URL Configuration
The relayer supports two ways to configure RPC URLs:
-
Public RPC URLs: These are the default RPC endpoints provided by the network. They are automatically selected based on the network configuration.
-
Custom RPC URL: You can specify a custom RPC URL using the
custom_rpc_urls
field in the relayer configuration. This is useful when you want to:-
Use your own RPC node
-
Use a paid service provider for better reliability and performance
-
Override the default public RPC URLs
-
Access custom network endpoints
-
When both are available, the relayer will:
1. First attempt to use the custom_rpc_urls
if configured
2. Fall back to the public RPC URLs if no custom URL is configured or if it’s not accessible
When using custom RPC URLs:
|
Important Considerations
This software is in alpha stage. Use in production environments at your own risk. |
Deployment Considerations
The OpenZeppelin Relayer is designed to function as a backend service and is not meant to be directly exposed to the public internet. To protect the service from unauthorized access, deploy it behind your own secure backend infrastructure—such as a reverse proxy or firewall—and restrict access to trusted internal components only. Direct exposure can increase the risk of exploitation and security breaches. |
Support
For support or inquiries, contact us on Telegram.
License
This project is licensed under the GNU Affero General Public License v3.0 - see the LICENSE file for details.
Security
For security concerns, please refer to our Security Policy.