- Notifications
You must be signed in to change notification settings - Fork52
OpenZeppelin/openzeppelin-relayer
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
This relayer service enables interaction with blockchain networks through transaction submissions. It offers multi-chain support and an extensible architecture for adding new chains.
- 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.
- Relayer Plugins: Extend the relayer functionality through TypeScript functions.
- Solana
- EVM
- Stellar
For details about current development status and upcoming features, check ourProject Roadmap.
View theInstallation documentation for detailed information. For a quicker introduction, check out theQuickstart guide.
View theUsage documentation for more information.
The repository includes several ready-to-use examples to help you get started with different configurations:
| Example | Description |
|---|---|
basic-example | Simple setup with Redis |
redis-storage | Simple setup with Redis for storage |
basic-example-logging | Configuration with file-based logging |
basic-example-metrics | Setup with Prometheus and Grafana metrics |
vault-secret-signer | Using HashiCorp Vault for key management |
vault-transit-signer | Using Vault Transit for secure signing |
evm-turnkey-signer | Using Turnkey Signer for EVM secure signing |
solana-turnkey-signer | Using Turnkey Signer for Solana secure signing |
solana-google-cloud-kms-signer | Using Google Cloud KMS Signer for Solana secure signing |
stellar-gcp-kms-signer | Using Google Cloud KMS Signer for Stellar secure signing |
evm-cdp-signer | Using CDP Signer for EVM secure signing |
network-configuration-config-file | Using Custom network configuration via config file |
network-configuration-json-file | Using Custom network configuration via json file |
aws-sqs-queue-storage | Local SQS queue backend setup using LocalStack |
x402-facilitator-plugin | x402 Facilitator plugin |
Each example includes:
- A README with step-by-step instructions
- Docker Compose configuration
- Required configuration files
The OpenZeppelin Relayer is built using Actix-web and provides HTTP endpoints for transaction submission, in-memory repository implementations, and configurable network policies.
The following diagram illustrates the architecture of the relayer service, highlighting key components and their interactions.
%%{init: { 'theme': 'base', 'themeVariables': { 'background': '#ffffff', 'mainBkg': '#ffffff', 'primaryBorderColor': '#cccccc' }}}%%flowchart TB subgraph "Clients" client[API/SDK] end subgraph "OpenZeppelin Relayer" subgraph "API Layer" api[API Routes & Controllers] middleware[Middleware] plugins[Relayer Plugins] end subgraph "Domain Layer" domain[Domain Logic] relayer[Relayer Services] policies[Policy Enforcement] end subgraph "Infrastructure" repositories[Repositories] jobs[Job Queue System] signer[Signer Services] provider[Network Providers] end subgraph "Services Layer" transaction[Transaction Services] vault[Vault Services] webhook[Webhook Notifications] monitoring[Monitoring & Metrics] end subgraph "Configuration" config_files[Config Files] env_vars[Environment Variables] end end subgraph "External Systems" blockchain[Blockchain Networks] redis[Redis] vault_ext[HashiCorp Vault] metrics[Prometheus/Grafana] notification[Notification Services] end %% Client connections client -- "HTTP Requests" --> api %% API Layer connections api -- "Processes requests" --> middleware middleware -- "Validates & routes" --> domain middleware -- "Invokes" --> plugins %% Domain Layer connections domain -- "Uses" --> relayer domain -- "Enforces" --> policies relayer -- "Processes" --> transaction plugins -- "Uses" --> relayer %% Services Layer connections transaction -- "Signs with" --> signer transaction -- "Connects via" --> provider transaction -- "Queues jobs" --> jobs webhook -- "Notifies" --> notification monitoring -- "Collects" --> metrics signer -- "May use" --> vault %% Infrastructure connections repositories -- "Stores data" --> redis jobs -- "Processes async" --> redis vault -- "Secrets management" --> vault_ext provider -- "Interacts with" --> blockchain %% Configuration connections config_files -- "Configures" --> domain env_vars -- "Configures" --> domain %% Styling classDef apiClass fill:#f9f,stroke:#333,stroke-width:2px classDef domainClass fill:#bbf,stroke:#333,stroke-width:2px classDef infraClass fill:#bfb,stroke:#333,stroke-width:2px classDef serviceClass fill:#fbf,stroke:#333,stroke-width:2px classDef configClass fill:#fbb,stroke:#333,stroke-width:2px classDef externalClass fill:#ddd,stroke:#333,stroke-width:1px class api,middleware,plugins apiClass class domain,relayer,policies domainClass class repositories,jobs,signer,provider infraClass class transaction,vault,webhook,monitoring serviceClass class config_files,env_vars configClass class blockchain,redis,vault_ext,metrics,notification externalClassThe 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│ ├── plugins/# Relayer plugins│ └── 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.)
- Docker
- Rust
- Redis
- Sodium
- Node.js + Typescript + ts-node (v20+) for plugins.
To get started, clone the repository:
git clone https://github.com/openzeppelin/openzeppelin-relayercd openzeppelin-relayerRun the following commands to install pre-commit hooks:
Install pre-commit hooks:
pip install pre-commitpre-commit install --install-hooks -t commit-msg -t pre-commit -t pre-push
⚠️ If you encounter issues with pip, consider usingpipx for a global installation.Install the toolchain:
rustup component add rustfmt
Install stable libsodium version fromhere.
Follow steps to install libsodium from thelibsodium installation guide.
Note (Debian/Ubuntu): If you're compiling libsodium from source, install build-essential first.
sudo apt-get update&& sudo apt-get install -y build-essential
Install Node.js fromhere.
Install Typescript and ts-node:
npm install -g typescript ts-node
To run tests, use the following commands:
cargotestcargotest propertiescargotest integration
⚠️ Debian/Ubuntu: If you encounter OpenSSL build errors, install the required packages:
sudo apt-get update&& sudo apt-get install -y pkg-config libssl-dev- You can start a Redis instance using the following command:
docker run -d \ --name redis \ -p 6379:6379 \ redis:latest
Then remove the
#[ignore = "Requires active Redis instance"]attribute from the tests you want to run.Run the tests using single thread to avoid race conditions within suites:
cargotest your_test_regex -- --test-threads=1### Config filesCreate`config/config.json` file. You can use`config/config.example.json` as a starting point:```shcp config/config.example.json config/config.json
Refer to theConfiguration References section for a complete list of configuration options.
Create.env with correct values according to your needs from.env.example file as a starting point:
cp .env.example .env
The relayer supports two queue backends:
redis(default): uses Apalis + Redis queuessqs: uses AWS SQS workers/cron and minimizes Apalis queue usage
Set in.env:
QUEUE_BACKEND=redis# or# QUEUE_BACKEND=sqs
When using SQS:
QUEUE_BACKEND=sqsAWS_REGION=us-east-1AWS_ACCOUNT_ID=123456789012# Optional: "auto" (default), "standard", or "fifo"# SQS_QUEUE_TYPE=auto# Optional alternative to AWS_ACCOUNT_ID:# SQS_QUEUE_URL_PREFIX=https://sqs.us-east-1.amazonaws.com/123456789012/relayer-
By default (SQS_QUEUE_TYPE=auto), the relayer auto-detects whether queues are standard or FIFO at startup.
Use distributed mode for multi-instance deployments so scheduled workers use Redis-based distributed locks and avoid duplicate execution:
DISTRIBUTED_MODE=true
For single-instance local development, keep:
DISTRIBUTED_MODE=false
Note: After the service is running, all configuration components (relayers, signers, notifications) can also be managed via REST API endpoints for runtime changes. See theConfiguration Guide for details on API-based configuration management.
To create a new signer keystore, use the provided key generation tool:
cargo run --example create_key -- \ --password DEFINE_YOUR_PASSWORD \ --output-dir config/keys \ --filename local-signer.json
Then update theKEYSTORE_PASSPHRASE field in your.env file with the password you used in the key creation example.
The tool supports the following options:
--password: Required. Must contain at least:- 12 characters
- One uppercase letter
- One lowercase letter
- One number
- One special character
--output-dir: Directory for the keystore file (creates if not exists)--filename: Optional. Uses timestamp-based name if not provided--force: Optional. Allows overwriting existing files
Example with all options:
cargo run --example create_key -- \ --password"YourSecurePassword123!" \ --output-dir config/keys \ --filename local-signer.json \ --force/config/config.json file is partially pre-configured. You need to specify the webhook URL that will receive updates from the relayer service.
For simplicity, visitWebhook.site, copy your unique URL, and then update the notifications[0].url field inconfig/config.json with this value.
To sign webhook notification payloads, populate theWEBHOOK_SIGNING_KEY entry in the.env file.
For development purposes, you can generate the signing key using:
cargo run --example generate_uuid
Note: Alternatively, you can use any online UUID generator.
Copy the generated UUID and update theWEBHOOK_SIGNING_KEY entry in the.env file.
Generate an API key signing key for development purposes using:
cargo run --example generate_uuid# or run this command to generate a UUID# uuidgen
Note: Alternatively, you can use any online UUID generator.
Copy the generated UUID and update theAPI_KEY entry in the.env file.
You can start Redis in one of two ways:
A.Expose to Host Only
Use this if only your host machine needs direct access to Redis (e.g., for local testing with redis-cli).
docker run -d \ --name redis \ -p 6379:6379 \ redis:latest
-p 6379:6379 binds the container port to your localhost on the same port.
B.Connect with Other Containers via Custom Network
Use this if relayer container need to talk to Redis.
docker run -d \ --name redis \ --network relayer-net \ redis:latest
--network relayer-net attaches Redis to the network you created in step 1.
Note: Make sure to create a dedicated network for the relayer and Redis containers to communicate. You can create a network using the following command
docker network create relayer-net.
In order to create and run plugins please follow thePlugins README file instructions.
Install dependencies:
cargo build
Run relayer:
cargo run
The service is available athttp://localhost:8080/api/v1
curl -X GET http://localhost:8080/api/v1/relayers \ -H"Content-Type: application/json" \ -H"AUTHORIZATION: Bearer YOUR_API_KEY"
If you usedocker-compose overdocker compose please readCompose V1 vs Compose V2 section.
Based on your.env file, docker compose may or may not start the metrics server ( within relayer app container), prometheus and grafana.
Note: If you want to start the metrics server, prometheus and grafana, make sure to set
METRICS_ENABLED=truein your.envfile.
If you want to start the services usingmake target, you can use the following command to start the services:
cargo make docker-compose-up
Note: By default docker compose command uses Dockerfile.development to build the image. If you want to use Dockerfile.production, you can set:
DOCKERFILE=Dockerfile.productionbefore runningcargo make docker-compose-up.
We have amake target to start the services with docker compose with metrics profile based on your.env file. For metrics server you will need to make sureMETRICS_ENABLED=true is set in your.env file. If you want to start the services directly using docker compose, you can use the following command:
# without metrics profile ( METRICS_ENABLED=false by default )# will only start the relayer app container and redis containerdocker compose up -d# or with metrics profile ( METRICS_ENABLED=true in .env file )# docker compose --profile metrics up -d
Make sure the containers are running without any restarts/issues:
docker ps -a
To stop the services, run the following command:
cargo make docker-compose-down# or# using docker compose without make target# without metrics profile# docker compose down# or with metrics profile# docker compose --profile metrics down
To check the logs of the services/containers, run the following command:
docker compose logs -f
- If you use
docker-composecommand, it will use Compose V1 by default which is deprecated. We recommend usingdocker composecommand. - You can read more about the differences between Compose V1 and Compose V2here.
- You can also check out the issuehere.
All the documentation is under
docs/directory.You can directly make changes to the specific files and raise a PR on this repo as well as ondocs repo for the content that is modified.
To generate technical rust documentation locally, run the following command
cargo make rust-docs
Rust docs will be generated in
docs/build/site/openzeppelin_relayer/directory.
- Currently we support logs and metrics ( uses prometheus and grafana) for the relayer server.
- For logs, our app defaults to writing logs to stdout/console. You can also configure it to write logs to a file path by setting
LOG_MODEtofile. Seedocker compose file for more details.
Metrics server is started on port
8081by default, which collects the metrics from the relayer server.Exposes list of metrics on the
/metricsendpoint.Note: By default, we don't map this port to the host machine. If you want to access the metrics server from the host machine, you can update the
docker-compose.yamlfile.Exposes
/debug/metrics/scrapeendpoint for prometheus to scrape metrics.
To view prometheus metrics in a UI, you can use
http://localhost:9090on your browser.To view grafana dashboard, you can use
http://localhost:3000on your browser.
We welcome contributions from the community! Here's how you can get involved:
- Fork the repository
- Create your feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
If you are looking for a good place to start, find a good first issuehere.
You can open an issue for abug report,feature request, ordocumentation request.
You can find more details in ourContributing guide.
Please read ourCode of Conduct and check theSecurity Policy for reporting vulnerabilities.
This project is licensed under the GNU Affero General Public License v3.0 - see theLICENSE file for details.
For security concerns, please refer to ourSecurity Policy.
The relayer includes built-in protection against Server-Side Request Forgery (SSRF) attacks when using custom RPC URLs. You can configure the following security features via environment variables:
RPC_ALLOWED_HOSTS: Comma-separated list of allowed RPC hostnames/IPs. If non-empty, only URLs with these hosts are permitted.- Example:
RPC_ALLOWED_HOSTS=eth-mainnet.g.alchemy.com,mainnet.infura.io
- Example:
RPC_BLOCK_PRIVATE_IPS: Block private IP addresses (RFC 1918, loopback, link-local). Set totrueto prevent RPC URLs from targeting private networks.- Example:
RPC_BLOCK_PRIVATE_IPS=true - Default:
false(for backwards compatibility)
- Example:
Note: Cloud metadata endpoints (169.254.169.254,fd00:ec2::254) arealways blocked to prevent credential theft, regardless of configuration.
Recommended Production Configuration:
RPC_BLOCK_PRIVATE_IPS=trueRPC_ALLOWED_HOSTS=eth-mainnet.g.alchemy.com,mainnet.infura.io,eth.llamarpc.com
See.env.example for more configuration examples.
If you have any questions, first see if the answer to your question can be found in theUser Documentation.
If the answer is not there:
We encourage you to reach out with any questions or feedback.
SeeCODEOWNERS file for the list of project maintainers.
About
OpenZeppelin Relayer
Topics
Resources
License
Code of conduct
Contributing
Security policy
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.