From 97e81f68d80ee57a671abe6949667888518a6869 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 19 Mar 2026 00:51:54 +0100 Subject: [PATCH 01/10] add docker configuration --- .env.example | 9 ++++++ DOCKER_README.md | 74 ++++++++++++--------------------------------- docker-compose.yaml | 32 +++++--------------- 3 files changed, 36 insertions(+), 79 deletions(-) diff --git a/.env.example b/.env.example index aa7f1438c..ec479ab73 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,15 @@ LOG_LEVEL=debug CONFIG_DIR=./config CONFIG_FILE_NAME=config.json + +# AWS dev credentials. There are 2 users each with their own +# kms key access: +# relayer-kms-signer with AWS_ACCESS_KEY_ID=AKIAWA.... +# relayer-kms-signer-dev with AWS_ACCESS_KEY_ID=AKIAWA.... + +AWS_ACCESS_KEY_ID=[AKIAWA....] +AWS_SECRET_ACCESS_KEY= + WEBHOOK_SIGNING_KEY= API_KEY= RATE_LIMIT_REQUESTS_PER_SECOND=100 diff --git a/DOCKER_README.md b/DOCKER_README.md index 797ee000b..73c00306e 100644 --- a/DOCKER_README.md +++ b/DOCKER_README.md @@ -17,74 +17,38 @@ This relayer service enables interaction with blockchain networks through transa > ⚠️ Redis is automatically started when using docker compose. If you are not using docker compose, you need to create a dedicated network and start redis manually. -## How to use images pushed to DockerHub +## Running Docker locally -- These images are automatically pulled when you use docker compose. See [using docker compose](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#running-services-with-docker-compose) for more information. -- If you are not using docker compose and you want to use these images, follow the steps below. - -### 1. Pull the image - -You can pull the latest image using the following command: +### 1. Setup env vars ```bash -docker pull openzeppelin/openzeppelin-relayer:latest +cp .env.example .env +uuidgen -> generates UUID ``` -### 2. Run the image +Create a unique uuid for WEBHOOK_SIGNING_KEY & API_KEY and set them in .env -You can run the image using the following command: +Set the correct AWS_ACCESS_KEY_ID & AWS_SECRET_ACCESS_KEY depending on user / relayer environment requires. + +AWS user `relayer-kms-signer-dev` + - has access to `origin-relayer-development-evm` + - with a public address: `0xca00ab46d0e009985c84c41e2f712c31102ff967` -```bash -docker run --env-file .env -d \ - --name relayer \ - --network relayer-net \ - -p 8080:8080 \ - -v ./config:/app/config:ro \ - openzeppelin/openzeppelin-relayer:latest -``` +AWS user `relayer-kms-signer` + - has access to `origin-relayer-production-evm` + - with a public address: `[todo....]` -### 3. Access the service - -Once the container is running, you can access the service at `http://localhost:8080`. - -You can test the relayer by sending a request using a curl call. See [testing relayer section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#test-the-relayer) for more information. - -### 4. Stop the container - -You can stop the container using the following command: +### 2. Start the service ```bash -docker stop relayer +docker compose up ``` -### 5. Remove the container - -You can remove the container using the following command: - -```bash -docker rm relayer -``` - -### 6. Remove the image +### 3. Access the service -You can remove the image using the following command: +Once the container is running, you can access the service at `http://localhost:8080`. ```bash -docker rmi openzeppelin/openzeppelin-relayer:latest +API_KEY=[set the api key] curl -s http://localhost:8080/api/v1/relayers \ + -H "Authorization: Bearer $API_KEY" | jq ``` - -## Contributing - -We welcome contributions to the OpenZeppelin Relayer. Please read our [contributing section](https://github.com/OpenZeppelin/openzeppelin-relayer/?tab=readme-ov-file#contributing) for more information. - -## Observability - -See the [observability section](https://github.com/OpenZeppelin/openzeppelin-relayer/?tab=readme-ov-file#observability) for more information on how to set up observability for the relayer. - -## License - -This project is licensed under the GNU Affero General Public License v3.0 - see the [LICENSE](https://github.com/OpenZeppelin/openzeppelin-relayer/blob/main/LICENSE) file for details. - -## Security - -For security concerns, please refer to our [Security Policy](https://github.com/OpenZeppelin/openzeppelin-relayer/blob/main/SECURITY.md). diff --git a/docker-compose.yaml b/docker-compose.yaml index a1ce9daec..2581ba659 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -10,8 +10,9 @@ services: - 8080:8080/tcp secrets: - api_key - - keystore_passphrase - webhook_signing_key + - aws_access_key_id + - aws_secret_access_key environment: APP_PORT: ${APP_PORT:-8080} METRICS_PORT: ${METRICS_PORT:-8081} @@ -20,7 +21,8 @@ services: RATE_LIMIT_BURST: ${RATE_LIMIT_BURST:-30} METRICS_ENABLED: ${METRICS_ENABLED:-false} WEBHOOK_SIGNING_KEY: ${WEBHOOK_SIGNING_KEY:-/dev/null} - KEYSTORE_PASSPHRASE: ${KEYSTORE_PASSPHRASE:-/dev/null} + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} API_KEY: ${API_KEY} # Options: trace, debug, info, warn, error # Default: info @@ -68,8 +70,6 @@ services: - REDIS_ADDR=redis://redis:6379 security_opt: - no-new-privileges - profiles: - - metrics depends_on: - redis networks: @@ -88,24 +88,6 @@ services: volumes: - ./cmd/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml restart: on-failure:5 - profiles: - - metrics - grafana: - image: grafana/grafana:11.5.1 - security_opt: - - no-new-privileges - ports: - - 3000:3000/tcp - networks: - - metrics-network - - relayer-network - volumes: - - ./cmd/prometheus/grafana.ini:/etc/grafana/grafana.ini - - ./cmd/prometheus/datasources:/etc/grafana/provisioning/datasources - - ./cmd/prometheus/dashboards:/etc/grafana/provisioning/dashboards - restart: on-failure:5 - profiles: - - metrics networks: metrics-network: internal: true @@ -119,5 +101,7 @@ secrets: environment: API_KEY webhook_signing_key: environment: WEBHOOK_SIGNING_KEY - keystore_passphrase: - environment: KEYSTORE_PASSPHRASE + aws_access_key_id: + environment: AWS_ACCESS_KEY_ID + aws_secret_access_key: + environment: AWS_SECRET_ACCESS_KEY From 466116e19338a65e56adda21139544d5b6de9a37 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 19 Mar 2026 12:30:34 +0100 Subject: [PATCH 02/10] some cleanup and add comments --- CODEOWNERS | 2 - CODE_OF_CONDUCT.md | 127 ------- CONTRIBUTING.md | 471 ------------------------ DOCKER_README.md | 54 --- Dockerfile.integration | 98 ----- README.md | 651 ++------------------------------- SECURITY.md | 32 -- config/config.development.json | 39 ++ config/config.example.json | 105 ------ config/config.production.json | 51 +++ config/networks/ethereum.json | 16 + docker-compose.integration.yml | 116 ------ 12 files changed, 139 insertions(+), 1623 deletions(-) delete mode 100644 CODEOWNERS delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 CONTRIBUTING.md delete mode 100644 DOCKER_README.md delete mode 100644 Dockerfile.integration delete mode 100644 SECURITY.md create mode 100644 config/config.development.json delete mode 100644 config/config.example.json create mode 100644 config/config.production.json delete mode 100644 docker-compose.integration.yml diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index 26fa9edb7..000000000 --- a/CODEOWNERS +++ /dev/null @@ -1,2 +0,0 @@ -* @OpenZeppelin/relayer-maintainers -SECURITY.md @OpenZeppelin/product-security @OpenZeppelin/relayer-maintainers diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index deb620477..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,127 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -- Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -- The use of sexualized language or imagery, and sexual attention or - advances of any kind -- Trolling, insulting or derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or email - address, without their explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement on [Telegram](t.me/openzeppelin_tg/2). -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 893dc702f..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,471 +0,0 @@ -# Contributing - -Thank you for your interest in contributing to the OpenZeppelin Relayer project! This document provides guidelines to ensure your contributions are effectively integrated into the project. - -There are many ways to contribute, regardless of your experience level. Whether you're new to Rust or a seasoned expert, your help is invaluable. Every contribution matters, no matter how small, and all efforts are greatly appreciated. This document is here to guide you through the process. Don’t feel overwhelmed—it’s meant to support and simplify your contribution journey. - -- [Contributing](#contributing) - - [Communication](#communication) - - [Development Workflow](#development-workflow) - - [GitHub workflow](#github-workflow) - - [1. Fork in the cloud](#1-fork-in-the-cloud) - - [2. Clone fork to local storage](#2-clone-fork-to-local-storage) - - [3. Create a Working Branch](#3-create-a-working-branch) - - [4. Keep your branch in sync](#4-keep-your-branch-in-sync) - - [5. Pre Commit Hooks](#5-pre-commit-hooks) - - [6. Commit Your Changes](#6-commit-your-changes) - - [7. Push to GitHub](#7-push-to-github) - - [8. Create a Pull Request](#8-create-a-pull-request) - - [Get a code review](#get-a-code-review) - - [Squash commits](#squash-commits) - - [Merging a commit](#merging-a-commit) - - [Reverting a commit](#reverting-a-commit) - - [Opening a Pull Request](#opening-a-pull-request) - - [Code Review](#code-review) - - [Best practices](#best-practices) - - [Coding Standards](#coding-standards) - - [Testing](#testing) - - [Security](#security) - - [Documentation](#documentation) - - [Issue and Pull Request Labeling Guidelines](#issue-and-pull-request-labeling-guidelines) - - [1. Area Labels (`A-`)](#1-area-labels-a-) - - [2. Type Labels (`T-`)](#2-type-labels-t-) - - [3. Priority Labels (`P-`)](#3-priority-labels-p-) - - [4. Status Labels (`S-`)](#4-status-labels-s-) - - [5. Difficulty Labels (`D-`)](#5-difficulty-labels-d-) - - [6. Other Useful Labels](#6-other-useful-labels) - - [How to Use These Labels](#how-to-use-these-labels) - - [License](#license) - - [Code of Conduct](#code-of-conduct) - -OpenZeppelin Relayer is open source and welcomes contributions from the community. - -As a potential contributor, your changes and ideas are welcome at any hour of the day or night, weekdays, weekends, and holidays. -Please do not ever hesitate to ask a question or send a pull request. - -Beginner focused information can be found below in [Open a Pull Request](#opening-a-pull-request) and [Code Review](#code-review). - -## Communication - -- [CODEOWNERS](./CODEOWNERS) -- [Telegram](t.me/openzeppelin_tg/2) -- [Website](https://openzeppelin.com/) -- [Blog](https://blog.openzeppelin.com/) -- [X](https://x.com/OpenZeppelin) - -## Development Workflow - -1. **Install Sodium**: - - Install stable libsodium version from [here](https://download.libsodium.org/libsodium/releases/). - - Follow steps to install libsodium from the [libsodium installation guide](https://doc.libsodium.org/installation). - -2. **Set Up Development Environment**: - - Install dependencies: - - ```sh - cargo build - ``` - - - Set up environment variables: - - ```sh - cp .env.example .env - ``` - -3. **Run Tests**: - - Unit tests: - - ```sh - cargo test - ``` - - - Integration tests: - - ```sh - cargo test integration - ``` - - > Note: If you run into any issues with the tests, run the tests with `RUST_TEST_THREADS=1` to avoid any racing conditions between tests. - - -4. **Configure Pre commit Hooks**: - - - Install & Configure Pre-Commit hooks - - ```sh - - # Use if you prefer to install it globally - - pip install pre-commit - pre-commit install --install-hooks -t commit-msg -t pre-commit -t pre-push - ``` - - > Note: If you run into issues with pip install, you may need [pipx](https://github.com/pypa/pipx?tab=readme-ov-file#install-pipx) to install pre-commit globally. - -## GitHub workflow - -### 1. Fork in the cloud - -- Visit -- Click `Fork` button (top right) to establish a cloud-based fork. - -### 2. Clone fork to local storage - -In your shell, define a local working directory as `working_dir`. - -```sh -export working_dir="${HOME}/repos" # Change to your preferred location for source code -``` - -Set `user` to match your github profile name: - -```sh -export user= -``` - -Create your clone: - -```sh -mkdir -p $working_dir -cd $working_dir -git clone https://github.com/$user/openzeppelin-relayer.git -# or: git clone git@github.com:$user/openzeppelin-relayer.git - -cd $working_dir/openzeppelin-relayer -git remote add upstream https://github.com/openzeppelin/openzeppelin-relayer.git -# or: git remote add upstream git@github.com:openzeppelin/openzeppelin-relayer.git - -# Never push to upstream main -git remote set-url --push upstream no_push - -# Confirm that your remotes make sense: -git remote -v -``` - -### 3. Create a Working Branch - -Get your local main up to date. - -```sh -cd $working_dir/openzeppelin-relayer -git fetch upstream -git checkout main -git rebase upstream/main -``` - -Create your new branch. - -```sh -git checkout -b myfeature -# or git switch -c myfeature -``` - -You may now edit files on the `myfeature` branch. - -### 4. Keep your branch in sync - -You will need to periodically fetch changes from the `upstream` -repository to keep your working branch in sync. - -Make sure your local repository is on your working branch and run the -following commands to keep it in sync: - -```sh -git fetch upstream -git rebase upstream/main -``` - -Please don't use `git pull` instead of the above `fetch` and -`rebase`. Since `git pull` executes a merge, it creates merge commits. These make the commit history messy -and violate the principle that commits ought to be individually understandable -and useful (see below). - -You might also consider changing your `.git/config` file via -`git config branch.autoSetupRebase always` to change the behavior of `git pull`, or another non-merge option such as `git pull --rebase`. - -### 5. Pre Commit Hooks - -We use pre-commit hooks to ensure that all code is formatted and linted correctly. - -We assume you already have `pipx` installed. If not, you can install it by following documentation [here](https://pipx.pypa.io/stable/installation/). - -To install and configure pre-commit hooks, run the following commands: - -```sh -# Use if you prefer to install it globally -pip install pre-commit -pre-commit install --install-hooks -t commit-msg -t pre-commit -t pre-push -``` - -This will install pre-commit hooks that will run on every commit and push. The hooks will check for linting, formatting, and other issues in your code. - -### 6. Commit Your Changes - -You will probably want to regularly commit your changes. It is likely that you will go back and edit, -build, and test multiple times. After a few cycles of this, you might -[amend your previous commit](https://www.w3schools.com/git/git_amend.asp). - -We use signed commits enforcement as a best practice. Make sure to sign your commits. This is a requirement for all commits. -You can read more about signing commits [here](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits). Also see telling git about your signing key [here](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key). - -Once you enable gpg signing globally in git, all commits will be signed by default. If you want to sign a commit manually, you can use the `-S` flag with the `git commit` command. - -```sh -git commit -``` - -### 7. Push to GitHub - -When your changes are ready for review, push your working branch to -your fork on GitHub. - -```sh -git push -f myfeature -``` - -### 8. Create a Pull Request - -- Visit your fork at `https://github.com//openzeppelin-relayer` -- Click the **Compare & Pull Request** button next to your `myfeature` branch. - -_If you have upstream write access_, please refrain from using the GitHub UI for -creating PRs, because GitHub will create the PR branch inside the main -repository rather than inside your fork. - -### Get a code review - -Once your pull request has been opened it will be assigned to one or more -reviewers. Those reviewers will do a thorough code review, looking for -correctness, bugs, opportunities for improvement, documentation and comments, -and style. - -Commit changes made in response to review comments to the same branch on your -fork. - -Very small PRs are easy to review. Very large PRs are very difficult to review. - -### Squash commits - -After a review, we automatically squash commits when merging a PR. This means that all commits in your PR will be combined into a single commit in the main branch. This is done to keep the commit history clean and easy to read. - -### Merging a commit - -Once you've received review and approval, your commits are squashed, your PR is ready for merging. - -Merging happens automatically after both a Reviewer and Approver have approved the PR. If you haven't squashed your commits, they may ask you to do so before approving a PR. - -### Reverting a commit - -In case you wish to revert a commit, use the following instructions. - -_If you have upstream write access_, please refrain from using the -`Revert` button in the GitHub UI for creating the PR, because GitHub -will create the PR branch inside the main repository rather than inside your fork. - -- Create a branch and sync it with upstream. - - ```sh - # create a branch - git checkout -b myrevert - - # sync the branch with upstream - git fetch upstream - git rebase upstream/main - ``` - -- If the commit you wish to revert is a _merge commit_, use this command: - - ```sh - # SHA is the hash of the merge commit you wish to revert - git revert -m 1 - ``` - - If it is a _single commit_, use this command: - - ```sh - # SHA is the hash of the single commit you wish to revert - git revert - ``` - -- This will create a new commit reverting the changes. Push this new commit to your remote. - - ```sh - git push myrevert - ``` - -- Finally, [create a Pull Request](#8-create-a-pull-request) using this branch. - -### Opening a Pull Request - -Pull requests are often called a "PR". -OpenZeppelin Relayer generally follows the standard [github pull request](https://help.github.com/articles/about-pull-requests/) process, but there is a layer of additional specific differences: - -Common new contributor PR issues are: - -- Dealing with test cases which fail on your PR, unrelated to the changes you introduce. -- Include mentions (like @person) and [keywords](https://help.github.com/en/articles/closing-issues-using-keywords) which could close the issue (like fixes #xxxx) in commit messages. - -## Code Review - -As a community we believe in the value of code review for all contributions. -Code review increases both the quality and readability of our codebase, which -in turn produces high quality software. - -As a community we expect that all active participants in the -community will also be active reviewers. - -There are two aspects of code review: giving and receiving. - -To make it easier for your PR to receive reviews, consider the reviewers will need you to: - -- Write [good commit messages](https://chris.beams.io/posts/git-commit/) -- Break large changes into a logical series of smaller patches which individually make easily understandable changes, and in aggregate solve a broader issue -- Label PRs: to do this read the messages the bot sends you to guide you through the PR process - -Reviewers, the people giving the review, are highly encouraged to revisit the [Code of Conduct](./CODE_OF_CONDUCT.md) and must go above and beyond to promote a collaborative, respectful community. -When reviewing PRs from others [The Gentle Art of Patch Review](http://sage.thesharps.us/2014/09/01/the-gentle-art-of-patch-review/) suggests an iterative series of focuses which is designed to lead new contributors to positive collaboration without inundating them initially with nuances: - -- Is the idea behind the contribution sound? -- Is the contribution architected correctly? -- Is the contribution polished? - -Note: if your pull request isn't getting enough attention, you can contact us on [Telegram](t.me/openzeppelin_tg/2) to get help finding reviewers. - -## Best practices - -- Write clear and meaningful git commit messages. -- If the PR will _completely_ fix a specific issue, include `fixes #123` in the PR body (where 123 is the specific issue number the PR will fix. This will automatically close the issue when the PR is merged. -- Make sure you don't include `@mentions` or `fixes` keywords in your git commit messages. These should be included in the PR body instead. -- When you make a PR for small change (such as fixing a typo, style change, or grammar fix), please squash your commits so that we can maintain a cleaner git history. -- Make sure you include a clear and detailed PR description explaining the reasons for the changes, and ensuring there is sufficient information for the reviewer to understand your PR. -- Additional Readings: - - [chris.beams.io/posts/git-commit/](https://chris.beams.io/posts/git-commit/) - - [github.com/blog/1506-closing-issues-via-pull-requests](https://github.com/blog/1506-closing-issues-via-pull-requests) - - [davidwalsh.name/squash-commits-git](https://davidwalsh.name/squash-commits-git) - - [https://mtlynch.io/code-review-love/](https://mtlynch.io/code-review-love/) - -## Coding Standards - -- Use **Rust 2021 edition**, version `1.88` or later. -- Follow the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/). -- Run pre-commit hooks on your code to ensure code quality. - -## Testing - -Testing is the responsibility of all contributors as such all contributions must pass existing tests and include new tests when applicable: - -1. Write tests for new features or bug fixes. -2. Run the test suite: - - ```sh - cargo test - ``` - -3. Ensure no warnings or errors. -4. Make sure you have test coverage for your code. You can run ```RUST_TEST_THREADS=1 cargo llvm-cov --locked --html --open``` to open the coverage report in the browser and verify the percentages for your code. Make sure to have a minimum of 80% coverage. - -## Security - -- Follow the stated [Security Policy](SECURITY.md). - -## Documentation - -- 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 on [docs](https://github.com/OpenZeppelin/docs) repo for the content that is modified. - -- To generate technical rust documentation locally, run the following command - - ```sh - cargo make rust-docs - ``` - -- Rust docs will be generated in `docs/build/site/openzeppelin_relayer/` directory. - - -## Issue and Pull Request Labeling Guidelines - -To ensure clarity and effective project management, we use a structured labeling system for issues and pull requests. Below are the label categories and their purposes: - -### 1. Area Labels (`A-`) - -These labels identify the part of the project the issue or PR pertains to: - -**`A-arch`**: High-level architectural concerns or changes. -**`A-clients`**: Issues related to blockchain clients (e.g., EVM, Solana, Stellar). -**`A-pipeline`**: Signer, Provider, and global Relayer services and CI pipelines. -**`A-configs`**: Issues related to `.env` files, relayer configuration, or network settings. -**`A-tests`**: Test setup and integration. -**`A-docs`**: Updates or fixes to project documentation. -**`A-deps`**: Pull requests that update a dependency file. - ---- - -### 2. Type Labels (`T-`) - -These labels describe the nature of the issue or PR: - -**`T-bug`**: Indicates a bug report. -**`T-feature`**: Suggests a new feature or enhancement. -**`T-task`**: General tasks or chores (e.g., refactoring, cleanup). -**`T-documentation`**: Issues or PRs related to documentation updates. -**`T-performance`**: Performance optimizations or bottlenecks. -**`T-security`**: Security vulnerabilities or related fixes. - ---- - -### 3. Priority Labels (`P-`) - -Define the priority level for addressing issues: - -**`P-high`**: Critical tasks or blockers. -**`P-medium`**: Important but not urgent. -**`P-low`**: Low-priority or non-urgent tasks. - ---- - -### 4. Status Labels (`S-`) - -Labels to track the workflow status of an issue: - -**`S-needs-triage`**: Requires initial triage or categorization. -**`S-in-progress`**: Actively being worked on. -**`S-blocked`**: Blocked by another issue or dependency. -**`S-needs-review`**: Awaiting review (code or design). -**`S-closed`**: Completed and closed issues. - ---- - -### 5. Difficulty Labels (`D-`) - -Indicate the complexity or effort required to address the issue: - -**`D-easy`**: Beginner-friendly tasks. -**`D-medium`**: Intermediate-level tasks. -**`D-hard`**: Complex or advanced issues. - ---- - -### 6. Other Useful Labels - -**`good-first-issue`**: Beginner-friendly, low-complexity issues to help new contributors. -**`help-wanted`**: Issues where community contributions are welcome. -**`discussion`**: Requires community or team input. -**`wontfix`**: This will not be worked on. -**`duplicate`**: This issue or pull request already exists. - ---- - -### How to Use These Labels - -When creating or triaging an issue or PR, apply the appropriate labels from the categories above. This helps maintain clarity, improve collaboration, and ensure smooth workflow management for all contributors. - -If you are unsure which label to apply, feel free to leave the issue or PR with the **`S-needs-triage`** label, and a maintainer will review it. - -## License - -By contributing to this project, you agree that your contributions will be licensed under the [AGPL-3.0 License](LICENSE). - -## Code of Conduct - -This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report any unacceptable behavior on [Telegram](t.me/openzeppelin_tg/2).` diff --git a/DOCKER_README.md b/DOCKER_README.md deleted file mode 100644 index 73c00306e..000000000 --- a/DOCKER_README.md +++ /dev/null @@ -1,54 +0,0 @@ -# OpenZeppelin Relayer - -This relayer service enables interaction with blockchain networks through transaction submissions. It offers multi-chain support and an extensible architecture for adding new chains. - -[User Docs](https://docs.openzeppelin.com/relayer/) | [Quickstart](https://docs.openzeppelin.com/relayer/quickstart) - -## Pre-requisites - -- Docker installed on your machine -- [Sodium](https://doc.libsodium.org/). See [install sodium section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#install-sodium) for more information. -- `.env` file with the required environment variables & `config/config.json` file with the required configuration. See how to set it up in [config files section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#config-files) for more information. -- Create signers and add them to the `config/config.json` file. See how to set it up in [signers section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#creating-a-signer) for more information. -- Configure webook url in `config/config.json` file. See how to set it up in [webhook section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#configure-webhook-url) for more information. -- Configure webhook signing key in `config/config.json` file. See how to set it up in [webhook section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#configure-webhook-signing-key) for more information. -- Configure Api key in `config/config.json` file. See how to set it up in [api key section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#configure-api-key) for more information. -- Redis server running. See how to set it up in [redis section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#starting-redis-manually-without-docker-compose) for more information. - -> ⚠️ Redis is automatically started when using docker compose. If you are not using docker compose, you need to create a dedicated network and start redis manually. - -## Running Docker locally - -### 1. Setup env vars - -```bash -cp .env.example .env -uuidgen -> generates UUID -``` - -Create a unique uuid for WEBHOOK_SIGNING_KEY & API_KEY and set them in .env - -Set the correct AWS_ACCESS_KEY_ID & AWS_SECRET_ACCESS_KEY depending on user / relayer environment requires. - -AWS user `relayer-kms-signer-dev` - - has access to `origin-relayer-development-evm` - - with a public address: `0xca00ab46d0e009985c84c41e2f712c31102ff967` - -AWS user `relayer-kms-signer` - - has access to `origin-relayer-production-evm` - - with a public address: `[todo....]` - -### 2. Start the service - -```bash -docker compose up -``` - -### 3. Access the service - -Once the container is running, you can access the service at `http://localhost:8080`. - -```bash -API_KEY=[set the api key] curl -s http://localhost:8080/api/v1/relayers \ - -H "Authorization: Bearer $API_KEY" | jq -``` diff --git a/Dockerfile.integration b/Dockerfile.integration deleted file mode 100644 index 937f39385..000000000 --- a/Dockerfile.integration +++ /dev/null @@ -1,98 +0,0 @@ -# Multi-stage Dockerfile for Integration Tests -# Uses Chainguard images for security and minimal footprint - -# ============================================================================ -# Builder Stage - Compile integration tests -# ============================================================================ -# Rustc v1.91.1 -# The digest needs to be updated everytime the minimum version is changed, and if the version is above 1.91.1 -FROM cgr.dev/chainguard/rust:latest-dev@sha256:33faad9a26e8437ed9725bea3eb2d1e85facd1c035a31af8d485ea8c0a935532 AS builder - -USER root -RUN apk update && apk --no-cache add \ - openssl-dev \ - perl \ - libsodium-dev - -WORKDIR /app - -# Install llvm-tools-preview component and cargo-llvm-cov for coverage -RUN rustup default stable && \ - rustup component add llvm-tools-preview && \ - cargo install cargo-llvm-cov --locked - -# Copy manifests -COPY Cargo.toml Cargo.lock ./ - -# Fetch dependencies (this layer will be cached) -RUN --mount=type=cache,target=/usr/local/cargo/registry \ - --mount=type=cache,target=/usr/local/cargo/git \ - cargo fetch --locked - -# Copy actual source -COPY src ./src -COPY tests ./tests - -# Build integration tests with coverage instrumentation -# Using RUSTFLAGS to enable coverage instrumentation during compilation -# This avoids the triple compilation issue (normal build, test build, coverage build) -RUN --mount=type=cache,target=/usr/local/cargo/registry \ - --mount=type=cache,target=/usr/local/cargo/git \ - --mount=type=cache,target=/app/target \ - RUSTFLAGS="-C instrument-coverage" \ - cargo test --features integration-tests --test integration --release --no-run && \ - TEST_BINARY=$(find target/release/deps -name 'integration-*' -type f -executable | head -n 1) && \ - cp "$TEST_BINARY" /app/integration-test-binary - -# ============================================================================ -# Runtime Stage - Minimal image with only what's needed to run tests and generate coverage -# ============================================================================ -FROM cgr.dev/chainguard/wolfi-base AS runtime - -WORKDIR /app - -# Copy LLVM tools and libraries from builder (architecture-agnostic with wildcards) -# This fixes the hardcoded aarch64 issue - works on both x86_64 and aarch64 -COPY --from=builder /root/.rustup/toolchains/stable-*/lib/rustlib/*/bin/llvm-cov \ - /usr/local/bin/llvm-cov -COPY --from=builder /root/.rustup/toolchains/stable-*/lib/rustlib/*/bin/llvm-profdata \ - /usr/local/bin/llvm-profdata - -# Copy LLVM shared libraries that llvm-cov and llvm-profdata depend on -# Use a broad pattern to cover libLLVM.so.* and libLLVM-*.so variants. -COPY --from=builder /root/.rustup/toolchains/stable-*/lib/libLLVM*.so* /usr/local/lib/ -COPY --from=builder /root/.rustup/toolchains/stable-*/lib/librustc_driver-*.so /usr/local/lib/ - -# Update library path so the LLVM tools can find their dependencies -ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH - -# Copy instrumented test binary from builder -COPY --from=builder /app/integration-test-binary ./integration-tests - -# Copy SSL libraries from builder -COPY --from=builder /usr/lib/libssl.so.3 /usr/lib/libssl.so.3 -COPY --from=builder /usr/lib/libcrypto.so.3 /usr/lib/libcrypto.so.3 - -# Copy test data files (contracts, etc.) -COPY tests/integration ./tests/integration - -# Create config and networks directory structure (will be mounted via docker-compose) -# Networks is mounted separately at /app/networks to avoid nested mount issues -RUN mkdir -p /app/config /app/networks - -# Create directories for coverage output with proper permissions -RUN mkdir -p /app/coverage /app/profraw && chmod -R 777 /app/coverage /app/profraw - -# Execute tests directly, then generate lcov report using llvm-cov -# This approach: -# 1. Runs the instrumented test binary (generates .profraw files) -# 2. Merges .profraw files into .profdata -# 3. Generates lcov report from .profdata -# All without invoking cargo or recompiling -CMD ["sh", "-c", "\ - LLVM_PROFILE_FILE=/app/profraw/integration-%p-%m.profraw \ - ./integration-tests --nocapture --test-threads=1 && \ - llvm-profdata merge -sparse /app/profraw/*.profraw -o /app/coverage/integration.profdata && \ - llvm-cov export --format=lcov \ - --instr-profile=/app/coverage/integration.profdata \ - ./integration-tests > /app/coverage/integration-lcov.info"] diff --git a/README.md b/README.md index 30f33ac47..d52ff9eaa 100644 --- a/README.md +++ b/README.md @@ -1,649 +1,64 @@ # OpenZeppelin Relayer -[![codecov](https://codecov.io/gh/OpenZeppelin/openzeppelin-relayer/graph/badge.svg?token=HKHIQNSJ6H)](https://codecov.io/gh/OpenZeppelin/openzeppelin-relayer) -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/OpenZeppelin/openzeppelin-relayer/badge)](https://api.securityscorecards.dev/projects/github.com/OpenZeppelin/openzeppelin-relayer) -[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) -[![CLA Assistant](https://github.com/OpenZeppelin/openzeppelin-relayer/actions/workflows/cla.yml/badge.svg)](https://github.com/OpenZeppelin/openzeppelin-relayer/actions/workflows/cla.yml) -[![CI](https://github.com/OpenZeppelin/openzeppelin-relayer/actions/workflows/ci.yaml/badge.svg)](https://github.com/OpenZeppelin/openzeppelin-relayer/actions/workflows/ci.yaml) -[![Release Workflow](https://github.com/OpenZeppelin/openzeppelin-relayer/actions/workflows/release-please.yml/badge.svg)](https://github.com/OpenZeppelin/openzeppelin-relayer/actions/workflows/release-please.yml) - This relayer service enables interaction with blockchain networks through transaction submissions. It offers multi-chain support and an extensible architecture for adding new chains. [User Docs](https://docs.openzeppelin.com/relayer/) | [Quickstart](https://docs.openzeppelin.com/relayer/quickstart) -## 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. -- **Relayer Plugins**: Extend the relayer functionality through TypeScript functions. - -## Supported networks - -- Solana -- EVM -- Stellar - -> For details about current development status and upcoming features, check our [Project Roadmap](https://docs.openzeppelin.com/relayer/roadmap). - -## For users - -### Installation - -View the [Installation](https://docs.openzeppelin.com/relayer#getting_started) documentation for detailed information. For a quicker introduction, check out the [Quickstart](https://docs.openzeppelin.com/relayer/quickstart) guide. - -### Usage - -View the [Usage](https://docs.openzeppelin.com/relayer#running_the_relayer) documentation for more information. - -### Examples - -The repository includes several ready-to-use examples to help you get started with different configurations: - -| Example | Description | -| ------------------------------------------------------------------------------------ | -------------------------------------------------------- | -| [`basic-example`](./examples/basic-example/) | Simple setup with Redis | -| [`redis-storage`](./examples/redis-storage/) | Simple setup with Redis for storage | -| [`redis-tls`](./examples/redis-tls/) | Redis with TLS encrypted connections | -| [`basic-example-logging`](./examples/basic-example-logging/) | Configuration with file-based logging | -| [`basic-example-metrics`](./examples/basic-example-metrics/) | Setup with Prometheus and Grafana metrics | -| [`vault-secret-signer`](./examples/vault-secret-signer/) | Using HashiCorp Vault for key management | -| [`vault-transit-signer`](./examples/vault-transit-signer/) | Using Vault Transit for secure signing | -| [`evm-turnkey-signer`](./examples/evm-turnkey-signer/) | Using Turnkey Signer for EVM secure signing | -| [`solana-turnkey-signer`](./examples/solana-turnkey-signer/) | Using Turnkey Signer for Solana secure signing | -| [`solana-google-cloud-kms-signer`](./examples/solana-google-cloud-kms-signer/) | Using Google Cloud KMS Signer for Solana secure signing | -| [`stellar-gcp-kms-signer`](./examples/stellar-gcp-kms-signer/) | Using Google Cloud KMS Signer for Stellar secure signing | -| [`evm-cdp-signer`](./examples/evm-cdp-signer/) | Using CDP Signer for EVM secure signing | -| [`network-configuration-config-file`](./examples/network-configuration-config-file/) | Using Custom network configuration via config file | -| [`network-configuration-json-file`](./examples/network-configuration-json-file/) | Using Custom network configuration via json file | -| [`aws-sqs-queue-storage`](./examples/aws-sqs-queue-storage/) | Local SQS queue backend setup using LocalStack | -| [`x402-facilitator-plugin`](./examples/x402-facilitator-plugin/) | x402 Facilitator plugin | - -Each example includes: - -- A README with step-by-step instructions -- Docker Compose configuration -- Required configuration files - -## For Developers - -### Technical Overview - -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. - -```mermaid -%%{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 externalClass -``` - -### Project Structure - -The project follows a standard Rust project layout: - -```sh -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.) -``` - -### Prerequisites - -- Docker -- Rust -- Redis -- [Sodium](https://doc.libsodium.org/) -- [Node.js + Typescript + ts-node](https://nodejs.org/) (v20+) for plugins. - -### Setup - -To get started, clone the repository: - -```sh -git clone https://github.com/openzeppelin/openzeppelin-relayer -cd openzeppelin-relayer -``` - -Run the following commands to install pre-commit hooks: - -- Install pre-commit hooks: - - ```bash - pip install pre-commit - pre-commit install --install-hooks -t commit-msg -t pre-commit -t pre-push - ``` - - > :warning: If you encounter issues with pip, consider using [pipx](https://pipx.pypa.io/stable/installation/) for a global installation. - -- Install the toolchain: - - ```sh - rustup component add rustfmt - ``` +## Pre-requisites -### Install Sodium +- Docker installed on your machine +- [Sodium](https://doc.libsodium.org/). See [install sodium section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#install-sodium) for more information. +- `.env` file with the required environment variables & `config/config.json` file with the required configuration. See how to set it up in [config files section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#config-files) for more information. +- Create signers and add them to the `config/config.json` file. See how to set it up in [signers section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#creating-a-signer) for more information. +- Configure webook url in `config/config.json` file. See how to set it up in [webhook section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#configure-webhook-url) for more information. +- Configure webhook signing key in `config/config.json` file. See how to set it up in [webhook section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#configure-webhook-signing-key) for more information. +- Configure Api key in `config/config.json` file. See how to set it up in [api key section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#configure-api-key) for more information. +- Redis server running. See how to set it up in [redis section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#starting-redis-manually-without-docker-compose) for more information. -- Install stable libsodium version from [here](https://download.libsodium.org/libsodium/releases/). -- Follow steps to install libsodium from the [libsodium installation guide](https://doc.libsodium.org/installation). +> ⚠️ Redis is automatically started when using docker compose. If you are not using docker compose, you need to create a dedicated network and start redis manually. - > Note (Debian/Ubuntu): If you're compiling libsodium from source, install build-essential first. +## Running Docker locally - ```bash - sudo apt-get update && sudo apt-get install -y build-essential - ``` - -### Install Node.js - -- Install Node.js from [here](https://nodejs.org/). -- Install Typescript and ts-node: - - ```bash - npm install -g typescript ts-node - ``` - -### Run Tests - -To run tests, use the following commands: - -```bash -cargo test -cargo test properties -cargo test integration -``` - -> :warning: Debian/Ubuntu: If you encounter OpenSSL build errors, install the required packages: - -```bash -sudo apt-get update && sudo apt-get install -y pkg-config libssl-dev -``` - -#### Run tests against Redis - -1. You can start a Redis instance using the following command: +### 1. Setup env vars ```bash -docker run -d \ - --name redis \ - -p 6379:6379 \ - redis:latest -``` - -2. Then remove the `#[ignore = "Requires active Redis instance"]` attribute from the tests you want to run. - -3. Run the tests using single thread to avoid race conditions within suites: - -````bash -cargo test your_test_regex -- --test-threads=1 - - -### Config files - -Create `config/config.json` file. You can use `config/config.example.json` as a starting point: - -```sh -cp config/config.example.json config/config.json -```` - -Refer to the [Configuration References](https://docs.openzeppelin.com/relayer#configuration_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: - -```sh cp .env.example .env +uuidgen -> generates UUID ``` -### Queue backend configuration (Redis or SQS) - -The relayer supports two queue backends: - -- `redis` (default): uses Apalis + Redis queues -- `sqs`: uses AWS SQS workers/cron and minimizes Apalis queue usage - -Set in `.env`: - -```bash -QUEUE_BACKEND=redis -# or -# QUEUE_BACKEND=sqs -``` - -When using SQS: - -```bash -QUEUE_BACKEND=sqs -AWS_REGION=us-east-1 -AWS_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: - -```bash -DISTRIBUTED_MODE=true -``` - -For single-instance local development, keep: - -```bash -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 the [Configuration Guide](https://docs.openzeppelin.com/relayer/configuration) for details on API-based configuration management. - -### Creating a Signer - -To create a new signer keystore, use the provided key generation tool: - -```sh -cargo run --example create_key -- \ - --password DEFINE_YOUR_PASSWORD \ - --output-dir config/keys \ - --filename local-signer.json -``` - -Then update the `KEYSTORE_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: - -```sh -cargo run --example create_key -- \ - --password "YourSecurePassword123!" \ - --output-dir config/keys \ - --filename local-signer.json \ - --force -``` - -### Configure Webhook URL - -`/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, visit [Webhook.site](https://webhook.site), copy your unique URL, and then update the notifications[0].url field in `config/config.json` with this value. +Create a unique uuid for WEBHOOK_SIGNING_KEY & API_KEY and set them in .env -### Configure Webhook Signing Key +Set the correct AWS_ACCESS_KEY_ID & AWS_SECRET_ACCESS_KEY depending on user / relayer environment requires. + +AWS user `relayer-kms-signer-dev` + - has access to `origin-relayer-development-evm` + - with a public address: `0xca00ab46d0e009985c84c41e2f712c31102ff967` -To sign webhook notification payloads, populate the `WEBHOOK_SIGNING_KEY` entry in the `.env` file. +AWS user `relayer-kms-signer` + - has access to `origin-relayer-production-evm` + - with a public address: `[todo....]` -For development purposes, you can generate the signing key using: +### 2. Cofigure the correct config file ```bash -cargo run --example generate_uuid -``` - -> Note: Alternatively, you can use any online UUID generator. - -Copy the generated UUID and update the `WEBHOOK_SIGNING_KEY` entry in the `.env` file. +# for development relayer +cp config/config.development.json config/config.json -### Configure API Key - -Generate an API key signing key for development purposes using: - -```bash -cargo run --example generate_uuid -# or run this command to generate a UUID -# uuidgen +# for production relayer +cp config/config.production.json config/config.json ``` -> Note: Alternatively, you can use any online UUID generator. - -Copy the generated UUID and update the `API_KEY` entry in the `.env` file. - -### Starting Redis manually (without docker compose) - -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). +### 3. Start the service ```bash -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. - -```sh -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`. - -## Configure a plugin - -In order to create and run plugins please follow the [Plugins README](./plugins/README.md) file instructions. - -## Running the relayer locally - -Install dependencies: - -```sh -cargo build -``` - -Run relayer: - -```sh -cargo run -``` - -## Test the Relayer - -The service is available at `http://localhost:8080/api/v1` - -```bash -curl -X GET http://localhost:8080/api/v1/relayers \ - -H "Content-Type: application/json" \ - -H "AUTHORIZATION: Bearer YOUR_API_KEY" -``` - -### Running services with docker compose - -If you use `docker-compose` over `docker compose` please read [Compose V1 vs Compose V2](#compose-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=true` in your `.env` file. - -If you want to start the services using [make](./Makefile.toml) target, you can use the following command to start the services: - -```sh -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.production` before running `cargo make docker-compose-up`. - -We have a [make](./Makefile.toml) target to start the services with docker compose with metrics profile based on your `.env` file. For metrics server you will need to make sure `METRICS_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: - -```sh -# without metrics profile ( METRICS_ENABLED=false by default ) -# will only start the relayer app container and redis container -docker 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: - -```sh -docker ps -a +docker compose up ``` -To stop the services, run the following command: - -```sh -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: - -```sh -docker compose logs -f -``` - -## Compose V1 vs Compose V2 - -- If you use `docker-compose` command, it will use Compose V1 by default which is deprecated. We recommend using `docker compose` command. -- You can read more about the differences between Compose V1 and Compose V2 [here](https://docs.docker.com/compose/intro/history/). -- You can also check out the issue [here](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/64). - -## Documentation - -- 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 on [docs](https://github.com/OpenZeppelin/docs) repo for the content that is modified. - -- To generate technical rust documentation locally, run the following command - - ```sh - cargo make rust-docs - ``` - -- Rust docs will be generated in `docs/build/site/openzeppelin_relayer/` directory. - -## Observability - -- Currently we support logs and metrics ( uses prometheus and grafana) for the relayer server. - -### Logs - -- 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_MODE` to `file`. See [docker compose file](./docker-compose.yaml) for more details. - -### Metrics - -- Metrics server is started on port `8081` by default, which collects the metrics from the relayer server. - - - Exposes list of metrics on the `/metrics` endpoint. - - > 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.yaml` file. - - - Exposes `/debug/metrics/scrape` endpoint for prometheus to scrape metrics. - -- To view prometheus metrics in a UI, you can use `http://localhost:9090` on your browser. - -- To view grafana dashboard, you can use `http://localhost:3000` on your browser. +### 4. Access the service -## Contributing - -We welcome contributions from the community! Here's how you can get involved: - -1. Fork the repository -2. Create your feature branch -3. Commit your changes -4. Push to the branch -5. Create a Pull Request - -If you are looking for a good place to start, find a good first issue [here](https://github.com/openzeppelin/openzeppelin-relayer/issues?q=is%3Aissue%20is%3Aopen%20label%3Agood-first-issue). - -You can open an issue for a [bug report](https://github.com/openzeppelin/openzeppelin-relayer/issues/new?assignees=&labels=T-bug%2CS-needs-triage&projects=&template=bug.yml), [feature request](https://github.com/openzeppelin/openzeppelin-relayer/issues/new?assignees=&labels=T-feature%2CS-needs-triage&projects=&template=feature.yml), or [documentation request](https://github.com/openzeppelin/openzeppelin-relayer/issues/new?assignees=&labels=T-documentation%2CS-needs-triage&projects=&template=docs.yml). - -You can find more details in our [Contributing](CONTRIBUTING.md) guide. - -Please read our [Code of Conduct](CODE_OF_CONDUCT.md) and check the [Security Policy](SECURITY.md) for reporting vulnerabilities. - -## License - -This project is licensed under the GNU Affero General Public License v3.0 - see the [LICENSE](LICENSE) file for details. - -## Security - -For security concerns, please refer to our [Security Policy](SECURITY.md). - -### Custom RPC URL Security - -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` - -- **`RPC_BLOCK_PRIVATE_IPS`**: Block private IP addresses (RFC 1918, loopback, link-local). Set to `true` to prevent RPC URLs from targeting private networks. - - Example: `RPC_BLOCK_PRIVATE_IPS=true` - - Default: `false` (for backwards compatibility) - -**Note:** Cloud metadata endpoints (`169.254.169.254`, `fd00:ec2::254`) are **always blocked** to prevent credential theft, regardless of configuration. - -**Recommended Production Configuration:** +Once the container is running, you can access the service at `http://localhost:8080`. ```bash -RPC_BLOCK_PRIVATE_IPS=true -RPC_ALLOWED_HOSTS=eth-mainnet.g.alchemy.com,mainnet.infura.io,eth.llamarpc.com +API_KEY=[set the api key] curl -s http://localhost:8080/api/v1/relayers \ + -H "Authorization: Bearer $API_KEY" | jq ``` - -See [`.env.example`](.env.example) for more configuration examples. - -## Get Help - -If you have any questions, first see if the answer to your question can be found in the [User Documentation](https://docs.openzeppelin.com/relayer/). - -If the answer is not there: - -- Join the [Telegram](https://t.me/openzeppelin_tg/2) to get help, or -- Open an issue with [the bug](https://github.com/openzeppelin/openzeppelin-relayer/issues/new?assignees=&labels=T-bug%2CS-needs-triage&projects=&template=bug.yml) - -We encourage you to reach out with any questions or feedback. - -## Maintainers - -See [CODEOWNERS](CODEOWNERS) file for the list of project maintainers. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index ef63a8330..000000000 --- a/SECURITY.md +++ /dev/null @@ -1,32 +0,0 @@ -# Security Policy - -Security vulnerabilities should be [disclosed](#reporting-a-vulnerability) to the [project maintainers](./CODEOWNERS), or alternatively by email to security@openzeppelin.com. - -## Supported Versions - -The following versions are currently supported and receive security updates. Alpha, Beta and Release candidates will not receive security updates. - -Security patches will be released for the latest minor of a given major release. For example, if an issue is found in versions >=1.13.0 and the latest is 1.14.0, the patch will be released only in version 1.14.1. - -Only critical severity bug fixes will be backported to past major releases. - -| Version | Supported | -| --------- | ------------------ | -| >= 0.1.x | :white_check_mark: | -| <= 0.0.9 | :x: | - -## Reporting a Vulnerability - -We're extremely grateful for security researchers and users that report vulnerabilities to us. -All reports are thoroughly investigated by the project's security team. - -Vulnerabilities are reported privately via GitHub's [Security Advisories](https://docs.github.com/en/code-security/security-advisories) feature. -Please use the following link to submit your vulnerability: [Report a vulnerability](https://github.com/openzeppelin/openzeppelin-relayer/security/advisories/new) - -Please see -[Privately reporting a security vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability) -for more information on how to submit a vulnerability using GitHub's interface. - -## Legal - -OpenZeppelin Relayer is made available under the GNU AGPL 3.0 License, which disclaims all warranties in relation to the project and which limits the liability of those that contribute and maintain the project, including OpenZeppelin. Your use of the project is also governed by the terms found at www.openzeppelin.com/tos (the "Terms"). As set out in the Terms, you are solely responsible for any use of OpenZeppelin Relayer and you assume all risks associated with any such use. This Security Policy in no way evidences or represents an on-going duty by any contributor, including OpenZeppelin, to correct any flaws or alert you to all or any of the potential risks of utilizing the project. diff --git a/config/config.development.json b/config/config.development.json new file mode 100644 index 000000000..a5ba7ec15 --- /dev/null +++ b/config/config.development.json @@ -0,0 +1,39 @@ +{ + "relayers": [ + { + "id": "hoodi-example", + "name": "Hoodi Relayer", + "network": "hoodi", + "paused": false, + "notification_id": "notification-example", + "signer_id": "relayer-kms-signer-dev", + "network_type": "evm", + "policies": { + "min_balance": 0 + } + } + ], + "notifications": [ + { + "id": "notification-example", + "type": "webhook", + "url": "https://example.com/webhook", + "signing_key": { + "type": "env", + "value": "WEBHOOK_SIGNING_KEY" + } + } + ], + "signers": [ + { + "id": "relayer-kms-signer-dev", + "type": "aws_kms", + "config": { + "region": "us-east-1", + "key_id": "arn:aws:kms:us-east-1:412463071885:key/mrk-248128595151466bb7f7b9a56501a98f" + } + } + ], + "networks": "./config/networks", + "plugins": [] +} diff --git a/config/config.example.json b/config/config.example.json deleted file mode 100644 index 3600bb3bb..000000000 --- a/config/config.example.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "relayers": [ - { - "id": "sepolia-example", - "name": "Sepolia Example", - "network": "sepolia", - "paused": false, - "notification_id": "notification-example", - "signer_id": "local-signer", - "network_type": "evm", - "policies": { - "min_balance": 0 - } - }, - { - "id": "stellar-example", - "name": "Stellar Example", - "network": "testnet", - "paused": false, - "notification_id": "notification-example", - "signer_id": "local-signer", - "network_type": "stellar" - }, - { - "id": "solana-example", - "name": "Solana Example", - "network": "devnet", - "paused": false, - "notification_id": "notification-example", - "signer_id": "local-signer", - "network_type": "solana", - "policies": { - "fee_payment_strategy": "user", - "min_balance": 0, - "swap_config": { - "strategy": "jupiter-swap", - "cron_schedule": "0 0 * * *", - "min_balance_threshold": 0 - }, - "allowed_programs": ["11111111111111111111111111111111", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"], - "allowed_tokens": [ - { - "mint": "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr", - "max_allowed_fee": 100000000, - "swap_config": { - "min_amount": 0, - "max_amount": 0, - "retain_min_amount": 0 - } - }, - { - "mint": "So11111111111111111111111111111111111111112" - } - ] - } - }, - { - "id": "solana-mainnet-example", - "name": "Solana Mainnet Example", - "network": "mainnet-beta", - "paused": false, - "notification_id": "notification-example", - "signer_id": "local-signer", - "network_type": "solana", - "policies": { - "min_balance": 0, - "allowed_tokens": [ - { - "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "max_allowed_fee": 100000000 - }, - { - "mint": "So11111111111111111111111111111111111111112" - } - ] - } - } - ], - "notifications": [ - { - "id": "notification-example", - "type": "webhook", - "url": "", - "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" - } - } - } - ], - "networks": "./config/networks", - "plugins": [] -} diff --git a/config/config.production.json b/config/config.production.json new file mode 100644 index 000000000..70fe45b7f --- /dev/null +++ b/config/config.production.json @@ -0,0 +1,51 @@ +{ + "relayers": [ + { + "id": "mainnet-relayer", + "name": "Mainnet Relayer", + "network": "mainnet", + "paused": false, + "notification_id": "notification-example", + "signer_id": "relayer-kms-signer", + "network_type": "evm", + "policies": { + "min_balance": 0 + } + }, + { + "id": "base-relayer", + "name": "Base Relayer", + "network": "base", + "paused": false, + "notification_id": "notification-example", + "signer_id": "relayer-kms-signer", + "network_type": "evm", + "policies": { + "min_balance": 0 + } + } + ], + "notifications": [ + { + "id": "notification-example", + "type": "webhook", + "url": "https://example.com/webhook", + "signing_key": { + "type": "env", + "value": "WEBHOOK_SIGNING_KEY" + } + } + ], + "signers": [ + { + "id": "relayer-kms-signer", + "type": "aws_kms", + "config": { + "region": "us-east-1", + "key_id": "arn:aws:kms:us-east-1:412463071885:key/mrk-248128595151466bb7f7b9a56501a98f" + } + } + ], + "networks": "./config/networks", + "plugins": [] +} diff --git a/config/networks/ethereum.json b/config/networks/ethereum.json index ac3e286b8..8b39786ae 100644 --- a/config/networks/ethereum.json +++ b/config/networks/ethereum.json @@ -60,6 +60,22 @@ "tags": [ "deprecated" ] + }, + { + "from": "mainnet", + "type": "evm", + "network": "hoodi", + "chain_id": 560048, + "explorer_urls": [ + "https://api-hoodi.etherscan.io/api", + "https://hoodi.etherscan.io" + ], + "is_testnet": true, + "required_confirmations": 6, + "rpc_urls": [ + "https://rpc.hoodi.ethpandaops.io" + ], + "tags": [] } ] } diff --git a/docker-compose.integration.yml b/docker-compose.integration.yml deleted file mode 100644 index 745a688f7..000000000 --- a/docker-compose.integration.yml +++ /dev/null @@ -1,116 +0,0 @@ ---- -services: - redis: - image: redis:7-alpine - container_name: integration-redis - command: - - redis-server - networks: - - integration - healthcheck: - test: - - CMD - - redis-cli - - ping - interval: 3s - timeout: 2s - retries: 5 - start_period: 5s - anvil: - profiles: - - local - image: ghcr.io/foundry-rs/foundry:stable - container_name: integration-anvil - entrypoint: '' - command: - - sh - - -c - - anvil --host 0.0.0.0 --chain-id 31337 --block-time 1 - ports: - - 0.0.0.0:8545:8545 - networks: - - integration - volumes: - - ./tests/integration/contracts:/contracts:ro - healthcheck: - test: - - CMD - - cast - - client - - --rpc-url - - http://localhost:8545 - interval: 2s - timeout: 2s - retries: 10 - start_period: 3s - relayer: - build: - context: . - dockerfile: Dockerfile.production - container_name: integration-relayer - depends_on: - redis: - condition: service_healthy - anvil: - condition: service_healthy - required: false # Only when local profile is active, but we handle startup order in script - ports: - - 8080:8080 - networks: - - integration - env_file: - - .env.integration - environment: - - REDIS_URL=redis://redis:6379 - - IN_DOCKER=true - # Reduce log verbosity during tests - only show WARN and ERROR - # This filters out verbose OpenTelemetry traces - - RUST_LOG=warn,openzeppelin_relayer=warn,actix_web=warn,tracing_actix_web=error - volumes: - - ${CONFIG_SOURCE:-./tests/integration/config/local}:/app/config:ro - - ./config/networks:/app/networks:ro - healthcheck: - test: - - CMD - - node - - -e - - "require('http').get({hostname:'localhost',port:8080,path:'/api/v1/health',headers:{'Authorization':'Bearer '+process.env.API_KEY}}, r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))" - interval: 5s - timeout: 3s - retries: 15 - start_period: 10s - integration-tests: - image: openzeppelin-relayer-integration:latest - build: - context: . - dockerfile: Dockerfile.integration - target: runtime # Use runtime stage that executes pre-built binary with coverage - container_name: integration-tests - user: ${HOST_UID:-1000}:${HOST_GID:-1000} # Run as host user for proper file permissions - depends_on: - relayer: - condition: service_healthy - networks: - - integration - env_file: - - .env.integration - environment: - - RELAYER_BASE_URL=http://relayer:8080 - # Test logging configuration - # Default to INFO level for clean output, can be overridden with RUST_LOG - - RUST_LOG=${RUST_LOG:-info} - # Test registry path (tests discover relayers via API) - - TEST_REGISTRY_PATH=${TEST_REGISTRY_PATH:-tests/integration/config/registry.json} - volumes: - - ./tests/integration/config:/app/config:ro - - ./config/networks:/app/networks:ro - # Mount coverage output directory - - ./coverage:/app/coverage - # Mount profraw directory for debugging raw coverage data - - ./profraw:/app/profraw -networks: - integration: - driver: bridge - name: openzeppelin-integration -volumes: - test-results: From e9ec80b4052899bb8e2a461ceee632757dde69d9 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 19 Mar 2026 22:07:52 +0100 Subject: [PATCH 03/10] some more README --- .env.railway.production.example | 24 ++++++++++++ README.md | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 .env.railway.production.example diff --git a/.env.railway.production.example b/.env.railway.production.example new file mode 100644 index 000000000..8d9f6486a --- /dev/null +++ b/.env.railway.production.example @@ -0,0 +1,24 @@ +# Railway production environment template +# Configure these in your Railway relayer service variables. + +# Load production config file (no file copy needed) +CONFIG_DIR=./config +CONFIG_FILE_NAME=config.production.json + +# Redis storage (redis-prod is the recommended Railway Redis service name) +REPOSITORY_STORAGE_TYPE=redis +REDIS_URL=${{redis-prod.REDIS_URL}} +REDIS_KEY_PREFIX=oz-relayer-prod +REDIS_CONNECTION_TIMEOUT_MS=10000 + +# Enable if running multiple relayer instances +DISTRIBUTED_MODE=true + +# Required application secrets +API_KEY= +WEBHOOK_SIGNING_KEY= +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= + +# Recommended: at-rest encryption for sensitive data in Redis +STORAGE_ENCRYPTION_KEY= diff --git a/README.md b/README.md index d52ff9eaa..f9d3986ab 100644 --- a/README.md +++ b/README.md @@ -62,3 +62,70 @@ Once the container is running, you can access the service at `http://localhost:8 API_KEY=[set the api key] curl -s http://localhost:8080/api/v1/relayers \ -H "Authorization: Bearer $API_KEY" | jq ``` + +## Railway production deployment (with Redis) + +Use Railway source builds with `Dockerfile.production`. A separate GitHub workflow for image builds is not required. + +Reference template: `.env.railway.production.example`. + +### 1. Provision Redis in Railway + +1. Add a Redis service from Railway templates. +2. Rename it clearly, for example: `redis-prod`. +3. Keep Redis internal/private (disable public TCP proxy unless you explicitly need external access). + +### 2. Configure relayer service variables + +Set these in the Railway service running this repo: + +```env +CONFIG_DIR=./config +CONFIG_FILE_NAME=config.production.json +REPOSITORY_STORAGE_TYPE=redis +REDIS_URL=${{redis-prod.REDIS_URL}} +REDIS_KEY_PREFIX=oz-relayer-prod +REDIS_CONNECTION_TIMEOUT_MS=10000 +``` + +Required application secrets (already needed by the relayer): + +```env +API_KEY= +WEBHOOK_SIGNING_KEY= +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +``` + +Recommended for production data protection: + +```env +STORAGE_ENCRYPTION_KEY= +DISTRIBUTED_MODE=true +``` + +### 3. Deploy and validate + +1. Trigger a Railway deployment. +2. Confirm logs do not contain `REDIS_URL must be set` or Redis connection errors. +3. Verify health endpoint: + +```bash +curl -s https:///api/v1/health +``` + +4. Verify relayers are loaded from `config.production.json`: + +```bash +curl -s https:///api/v1/relayers \ + -H "Authorization: Bearer " | jq +``` + +Expected: both `mainnet` and `base` relayers are present. + +### 4. Backups and restore runbook + +1. Enable Redis backups/snapshots in Railway. +2. During incident recovery, restore the latest healthy snapshot on Redis. +3. Redeploy the relayer service to re-establish clean connections. +4. Re-run the health and relayer validation calls above. From a67d9f88fad9c0bb21ac021ada77f4e48fb1f801 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 19 Mar 2026 22:16:07 +0100 Subject: [PATCH 04/10] fix cache mounts --- Dockerfile.development | 6 +++--- Dockerfile.production | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile.development b/Dockerfile.development index a1fdf50ab..f2468bf47 100644 --- a/Dockerfile.development +++ b/Dockerfile.development @@ -19,9 +19,9 @@ ARG CARGO_FEATURES="" # Copy COPY . . -RUN --mount=type=cache,target=/usr/local/cargo/registry \ - --mount=type=cache,target=/usr/local/cargo/git \ - --mount=type=cache,target=/usr/app/target \ +RUN --mount=type=cache,id=ozr-cargo-registry,target=/usr/local/cargo/registry \ + --mount=type=cache,id=ozr-cargo-git,target=/usr/local/cargo/git \ + --mount=type=cache,id=ozr-cargo-target,target=/usr/app/target \ if [ -n "$CARGO_FEATURES" ]; then \ cargo install --root /usr/app --path . --debug --locked --features "$CARGO_FEATURES"; \ else \ diff --git a/Dockerfile.production b/Dockerfile.production index 7f88f92fb..ed7a447da 100644 --- a/Dockerfile.production +++ b/Dockerfile.production @@ -15,9 +15,9 @@ WORKDIR /usr/app ARG CARGO_FEATURES="" COPY . . -RUN --mount=type=cache,target=/usr/local/cargo/registry \ - --mount=type=cache,target=/usr/local/cargo/git \ - --mount=type=cache,target=/usr/app/target \ +RUN --mount=type=cache,id=ozr-cargo-registry,target=/usr/local/cargo/registry \ + --mount=type=cache,id=ozr-cargo-git,target=/usr/local/cargo/git \ + --mount=type=cache,id=ozr-cargo-target,target=/usr/app/target \ if [ -n "$CARGO_FEATURES" ]; then \ cargo install --root /usr/app --path . --locked --features "$CARGO_FEATURES"; \ else \ From ed7a12c761dea5c8deb0640a8a1e2354e2ffabf3 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 19 Mar 2026 22:18:18 +0100 Subject: [PATCH 05/10] cache mount fix --- Dockerfile.development | 5 +---- Dockerfile.production | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Dockerfile.development b/Dockerfile.development index f2468bf47..4a09477a1 100644 --- a/Dockerfile.development +++ b/Dockerfile.development @@ -19,10 +19,7 @@ ARG CARGO_FEATURES="" # Copy COPY . . -RUN --mount=type=cache,id=ozr-cargo-registry,target=/usr/local/cargo/registry \ - --mount=type=cache,id=ozr-cargo-git,target=/usr/local/cargo/git \ - --mount=type=cache,id=ozr-cargo-target,target=/usr/app/target \ - if [ -n "$CARGO_FEATURES" ]; then \ +RUN if [ -n "$CARGO_FEATURES" ]; then \ cargo install --root /usr/app --path . --debug --locked --features "$CARGO_FEATURES"; \ else \ cargo install --root /usr/app --path . --debug --locked; \ diff --git a/Dockerfile.production b/Dockerfile.production index ed7a447da..7093b6626 100644 --- a/Dockerfile.production +++ b/Dockerfile.production @@ -15,10 +15,7 @@ WORKDIR /usr/app ARG CARGO_FEATURES="" COPY . . -RUN --mount=type=cache,id=ozr-cargo-registry,target=/usr/local/cargo/registry \ - --mount=type=cache,id=ozr-cargo-git,target=/usr/local/cargo/git \ - --mount=type=cache,id=ozr-cargo-target,target=/usr/app/target \ - if [ -n "$CARGO_FEATURES" ]; then \ +RUN if [ -n "$CARGO_FEATURES" ]; then \ cargo install --root /usr/app --path . --locked --features "$CARGO_FEATURES"; \ else \ cargo install --root /usr/app --path . --locked; \ From 8b09bbff13fd338838aaf697e1b1b33dee53c040 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 19 Mar 2026 22:25:55 +0100 Subject: [PATCH 06/10] fix config file problem --- Dockerfile.development | 1 + Dockerfile.production | 1 + 2 files changed, 2 insertions(+) diff --git a/Dockerfile.development b/Dockerfile.development index 4a09477a1..022703da4 100644 --- a/Dockerfile.development +++ b/Dockerfile.development @@ -31,6 +31,7 @@ FROM cgr.dev/chainguard/wolfi-base WORKDIR /app COPY --from=base --chown=nonroot:nonroot /usr/app/bin/openzeppelin-relayer /app/openzeppelin-relayer +COPY --from=base --chown=nonroot:nonroot /usr/app/config /app/config COPY --from=base /usr/lib/libssl.so.3 /usr/lib/libssl.so.3 COPY --from=base /usr/lib/libcrypto.so.3 /usr/lib/libcrypto.so.3 diff --git a/Dockerfile.production b/Dockerfile.production index 7093b6626..18c393daf 100644 --- a/Dockerfile.production +++ b/Dockerfile.production @@ -26,6 +26,7 @@ FROM cgr.dev/chainguard/wolfi-base WORKDIR /app COPY --from=base --chown=nonroot:nonroot /usr/app/bin/openzeppelin-relayer /app/openzeppelin-relayer +COPY --from=base --chown=nonroot:nonroot /usr/app/config /app/config COPY --from=base /usr/lib/libssl.so.3 /usr/lib/libssl.so.3 COPY --from=base /usr/lib/libcrypto.so.3 /usr/lib/libcrypto.so.3 From b55492d6a8f94cab77a61e538feaa39f62fb9893 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 19 Mar 2026 22:58:37 +0100 Subject: [PATCH 07/10] add redis storage encryption config --- .env.example | 4 ++++ .env.railway.production.example | 5 +++-- README.md | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index ec479ab73..c9b9dbd5f 100644 --- a/.env.example +++ b/.env.example @@ -22,6 +22,10 @@ REDIS_URL=redis://localhost:6379 # REDIS_READER_URL=redis://localhost:6380 REDIS_CONNECTION_TIMEOUT_MS=10000 REDIS_KEY_PREFIX=oz-relayer +# Required when REPOSITORY_STORAGE_TYPE=redis (application fails to start if missing) +# Generate with: openssl rand -base64 32 +# Alternative: cargo run --example generate_encryption_key +STORAGE_ENCRYPTION_KEY= # Queue backend selection # Supported: redis, sqs diff --git a/.env.railway.production.example b/.env.railway.production.example index 8d9f6486a..98eaac188 100644 --- a/.env.railway.production.example +++ b/.env.railway.production.example @@ -19,6 +19,7 @@ API_KEY= WEBHOOK_SIGNING_KEY= AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= - -# Recommended: at-rest encryption for sensitive data in Redis +# Required for Redis repository storage: +# Generate with: openssl rand -base64 32 +# Alternative: cargo run --example generate_encryption_key STORAGE_ENCRYPTION_KEY= diff --git a/README.md b/README.md index f9d3986ab..bd5dc5a59 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,11 @@ This relayer service enables interaction with blockchain networks through transa ```bash cp .env.example .env uuidgen -> generates UUID +openssl rand -base64 32 -> generates STORAGE_ENCRYPTION_KEY ``` Create a unique uuid for WEBHOOK_SIGNING_KEY & API_KEY and set them in .env +Set STORAGE_ENCRYPTION_KEY in `.env` when using Redis repository storage (`REPOSITORY_STORAGE_TYPE=redis`). Set the correct AWS_ACCESS_KEY_ID & AWS_SECRET_ACCESS_KEY depending on user / relayer environment requires. @@ -95,12 +97,20 @@ API_KEY= WEBHOOK_SIGNING_KEY= AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= +STORAGE_ENCRYPTION_KEY= +``` + +Generate the encryption key with one of: + +```bash +openssl rand -base64 32 +# or +cargo run --example generate_encryption_key ``` -Recommended for production data protection: +Recommended for production reliability: ```env -STORAGE_ENCRYPTION_KEY= DISTRIBUTED_MODE=true ``` From ecf354860829056fe3aa96883af3d6731ffd802a Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 19 Mar 2026 23:06:50 +0100 Subject: [PATCH 08/10] add some comments --- README.md | 1 + docs/README.md | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index bd5dc5a59..c9780b9d4 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ API_KEY=[set the api key] curl -s http://localhost:8080/api/v1/relayers \ ## Railway production deployment (with Redis) Use Railway source builds with `Dockerfile.production`. A separate GitHub workflow for image builds is not required. +This Railway service is configured to auto-deploy from GitHub on pushes to the `production` branch (each push triggers a Docker build + deployment). Reference template: `.env.railway.production.example`. diff --git a/docs/README.md b/docs/README.md index ed97eb2b4..5f3648441 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,10 @@ # Generate Documentation +## Deployment note + +- The Railway production service is configured with GitHub auto-deploy on the `production` branch. +- A push to `production` automatically triggers Docker build and deployment. + - To generate rust documentation locally, run the following command - In separate terminal from root of the repo run: From a08da65f4dd391ca0e6d2ff4c73d97e7766d670e Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Fri, 20 Mar 2026 12:43:31 +0100 Subject: [PATCH 09/10] restore files back --- CODEOWNERS | 2 + CODE_OF_CONDUCT.md | 127 +++++++++ CONTRIBUTING.md | 471 +++++++++++++++++++++++++++++++++ DOCKER_README.md | 90 +++++++ Dockerfile.integration | 98 +++++++ SECURITY.md | 32 +++ docker-compose.integration.yml | 116 ++++++++ 7 files changed, 936 insertions(+) create mode 100644 CODEOWNERS create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 DOCKER_README.md create mode 100644 Dockerfile.integration create mode 100644 SECURITY.md create mode 100644 docker-compose.integration.yml diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000..26fa9edb7 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +* @OpenZeppelin/relayer-maintainers +SECURITY.md @OpenZeppelin/product-security @OpenZeppelin/relayer-maintainers diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..deb620477 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,127 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or + advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email + address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement on [Telegram](t.me/openzeppelin_tg/2). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..893dc702f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,471 @@ +# Contributing + +Thank you for your interest in contributing to the OpenZeppelin Relayer project! This document provides guidelines to ensure your contributions are effectively integrated into the project. + +There are many ways to contribute, regardless of your experience level. Whether you're new to Rust or a seasoned expert, your help is invaluable. Every contribution matters, no matter how small, and all efforts are greatly appreciated. This document is here to guide you through the process. Don’t feel overwhelmed—it’s meant to support and simplify your contribution journey. + +- [Contributing](#contributing) + - [Communication](#communication) + - [Development Workflow](#development-workflow) + - [GitHub workflow](#github-workflow) + - [1. Fork in the cloud](#1-fork-in-the-cloud) + - [2. Clone fork to local storage](#2-clone-fork-to-local-storage) + - [3. Create a Working Branch](#3-create-a-working-branch) + - [4. Keep your branch in sync](#4-keep-your-branch-in-sync) + - [5. Pre Commit Hooks](#5-pre-commit-hooks) + - [6. Commit Your Changes](#6-commit-your-changes) + - [7. Push to GitHub](#7-push-to-github) + - [8. Create a Pull Request](#8-create-a-pull-request) + - [Get a code review](#get-a-code-review) + - [Squash commits](#squash-commits) + - [Merging a commit](#merging-a-commit) + - [Reverting a commit](#reverting-a-commit) + - [Opening a Pull Request](#opening-a-pull-request) + - [Code Review](#code-review) + - [Best practices](#best-practices) + - [Coding Standards](#coding-standards) + - [Testing](#testing) + - [Security](#security) + - [Documentation](#documentation) + - [Issue and Pull Request Labeling Guidelines](#issue-and-pull-request-labeling-guidelines) + - [1. Area Labels (`A-`)](#1-area-labels-a-) + - [2. Type Labels (`T-`)](#2-type-labels-t-) + - [3. Priority Labels (`P-`)](#3-priority-labels-p-) + - [4. Status Labels (`S-`)](#4-status-labels-s-) + - [5. Difficulty Labels (`D-`)](#5-difficulty-labels-d-) + - [6. Other Useful Labels](#6-other-useful-labels) + - [How to Use These Labels](#how-to-use-these-labels) + - [License](#license) + - [Code of Conduct](#code-of-conduct) + +OpenZeppelin Relayer is open source and welcomes contributions from the community. + +As a potential contributor, your changes and ideas are welcome at any hour of the day or night, weekdays, weekends, and holidays. +Please do not ever hesitate to ask a question or send a pull request. + +Beginner focused information can be found below in [Open a Pull Request](#opening-a-pull-request) and [Code Review](#code-review). + +## Communication + +- [CODEOWNERS](./CODEOWNERS) +- [Telegram](t.me/openzeppelin_tg/2) +- [Website](https://openzeppelin.com/) +- [Blog](https://blog.openzeppelin.com/) +- [X](https://x.com/OpenZeppelin) + +## Development Workflow + +1. **Install Sodium**: + - Install stable libsodium version from [here](https://download.libsodium.org/libsodium/releases/). + - Follow steps to install libsodium from the [libsodium installation guide](https://doc.libsodium.org/installation). + +2. **Set Up Development Environment**: + - Install dependencies: + + ```sh + cargo build + ``` + + - Set up environment variables: + + ```sh + cp .env.example .env + ``` + +3. **Run Tests**: + - Unit tests: + + ```sh + cargo test + ``` + + - Integration tests: + + ```sh + cargo test integration + ``` + + > Note: If you run into any issues with the tests, run the tests with `RUST_TEST_THREADS=1` to avoid any racing conditions between tests. + + +4. **Configure Pre commit Hooks**: + + - Install & Configure Pre-Commit hooks + + ```sh + + # Use if you prefer to install it globally + + pip install pre-commit + pre-commit install --install-hooks -t commit-msg -t pre-commit -t pre-push + ``` + + > Note: If you run into issues with pip install, you may need [pipx](https://github.com/pypa/pipx?tab=readme-ov-file#install-pipx) to install pre-commit globally. + +## GitHub workflow + +### 1. Fork in the cloud + +- Visit +- Click `Fork` button (top right) to establish a cloud-based fork. + +### 2. Clone fork to local storage + +In your shell, define a local working directory as `working_dir`. + +```sh +export working_dir="${HOME}/repos" # Change to your preferred location for source code +``` + +Set `user` to match your github profile name: + +```sh +export user= +``` + +Create your clone: + +```sh +mkdir -p $working_dir +cd $working_dir +git clone https://github.com/$user/openzeppelin-relayer.git +# or: git clone git@github.com:$user/openzeppelin-relayer.git + +cd $working_dir/openzeppelin-relayer +git remote add upstream https://github.com/openzeppelin/openzeppelin-relayer.git +# or: git remote add upstream git@github.com:openzeppelin/openzeppelin-relayer.git + +# Never push to upstream main +git remote set-url --push upstream no_push + +# Confirm that your remotes make sense: +git remote -v +``` + +### 3. Create a Working Branch + +Get your local main up to date. + +```sh +cd $working_dir/openzeppelin-relayer +git fetch upstream +git checkout main +git rebase upstream/main +``` + +Create your new branch. + +```sh +git checkout -b myfeature +# or git switch -c myfeature +``` + +You may now edit files on the `myfeature` branch. + +### 4. Keep your branch in sync + +You will need to periodically fetch changes from the `upstream` +repository to keep your working branch in sync. + +Make sure your local repository is on your working branch and run the +following commands to keep it in sync: + +```sh +git fetch upstream +git rebase upstream/main +``` + +Please don't use `git pull` instead of the above `fetch` and +`rebase`. Since `git pull` executes a merge, it creates merge commits. These make the commit history messy +and violate the principle that commits ought to be individually understandable +and useful (see below). + +You might also consider changing your `.git/config` file via +`git config branch.autoSetupRebase always` to change the behavior of `git pull`, or another non-merge option such as `git pull --rebase`. + +### 5. Pre Commit Hooks + +We use pre-commit hooks to ensure that all code is formatted and linted correctly. + +We assume you already have `pipx` installed. If not, you can install it by following documentation [here](https://pipx.pypa.io/stable/installation/). + +To install and configure pre-commit hooks, run the following commands: + +```sh +# Use if you prefer to install it globally +pip install pre-commit +pre-commit install --install-hooks -t commit-msg -t pre-commit -t pre-push +``` + +This will install pre-commit hooks that will run on every commit and push. The hooks will check for linting, formatting, and other issues in your code. + +### 6. Commit Your Changes + +You will probably want to regularly commit your changes. It is likely that you will go back and edit, +build, and test multiple times. After a few cycles of this, you might +[amend your previous commit](https://www.w3schools.com/git/git_amend.asp). + +We use signed commits enforcement as a best practice. Make sure to sign your commits. This is a requirement for all commits. +You can read more about signing commits [here](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits). Also see telling git about your signing key [here](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key). + +Once you enable gpg signing globally in git, all commits will be signed by default. If you want to sign a commit manually, you can use the `-S` flag with the `git commit` command. + +```sh +git commit +``` + +### 7. Push to GitHub + +When your changes are ready for review, push your working branch to +your fork on GitHub. + +```sh +git push -f myfeature +``` + +### 8. Create a Pull Request + +- Visit your fork at `https://github.com//openzeppelin-relayer` +- Click the **Compare & Pull Request** button next to your `myfeature` branch. + +_If you have upstream write access_, please refrain from using the GitHub UI for +creating PRs, because GitHub will create the PR branch inside the main +repository rather than inside your fork. + +### Get a code review + +Once your pull request has been opened it will be assigned to one or more +reviewers. Those reviewers will do a thorough code review, looking for +correctness, bugs, opportunities for improvement, documentation and comments, +and style. + +Commit changes made in response to review comments to the same branch on your +fork. + +Very small PRs are easy to review. Very large PRs are very difficult to review. + +### Squash commits + +After a review, we automatically squash commits when merging a PR. This means that all commits in your PR will be combined into a single commit in the main branch. This is done to keep the commit history clean and easy to read. + +### Merging a commit + +Once you've received review and approval, your commits are squashed, your PR is ready for merging. + +Merging happens automatically after both a Reviewer and Approver have approved the PR. If you haven't squashed your commits, they may ask you to do so before approving a PR. + +### Reverting a commit + +In case you wish to revert a commit, use the following instructions. + +_If you have upstream write access_, please refrain from using the +`Revert` button in the GitHub UI for creating the PR, because GitHub +will create the PR branch inside the main repository rather than inside your fork. + +- Create a branch and sync it with upstream. + + ```sh + # create a branch + git checkout -b myrevert + + # sync the branch with upstream + git fetch upstream + git rebase upstream/main + ``` + +- If the commit you wish to revert is a _merge commit_, use this command: + + ```sh + # SHA is the hash of the merge commit you wish to revert + git revert -m 1 + ``` + + If it is a _single commit_, use this command: + + ```sh + # SHA is the hash of the single commit you wish to revert + git revert + ``` + +- This will create a new commit reverting the changes. Push this new commit to your remote. + + ```sh + git push myrevert + ``` + +- Finally, [create a Pull Request](#8-create-a-pull-request) using this branch. + +### Opening a Pull Request + +Pull requests are often called a "PR". +OpenZeppelin Relayer generally follows the standard [github pull request](https://help.github.com/articles/about-pull-requests/) process, but there is a layer of additional specific differences: + +Common new contributor PR issues are: + +- Dealing with test cases which fail on your PR, unrelated to the changes you introduce. +- Include mentions (like @person) and [keywords](https://help.github.com/en/articles/closing-issues-using-keywords) which could close the issue (like fixes #xxxx) in commit messages. + +## Code Review + +As a community we believe in the value of code review for all contributions. +Code review increases both the quality and readability of our codebase, which +in turn produces high quality software. + +As a community we expect that all active participants in the +community will also be active reviewers. + +There are two aspects of code review: giving and receiving. + +To make it easier for your PR to receive reviews, consider the reviewers will need you to: + +- Write [good commit messages](https://chris.beams.io/posts/git-commit/) +- Break large changes into a logical series of smaller patches which individually make easily understandable changes, and in aggregate solve a broader issue +- Label PRs: to do this read the messages the bot sends you to guide you through the PR process + +Reviewers, the people giving the review, are highly encouraged to revisit the [Code of Conduct](./CODE_OF_CONDUCT.md) and must go above and beyond to promote a collaborative, respectful community. +When reviewing PRs from others [The Gentle Art of Patch Review](http://sage.thesharps.us/2014/09/01/the-gentle-art-of-patch-review/) suggests an iterative series of focuses which is designed to lead new contributors to positive collaboration without inundating them initially with nuances: + +- Is the idea behind the contribution sound? +- Is the contribution architected correctly? +- Is the contribution polished? + +Note: if your pull request isn't getting enough attention, you can contact us on [Telegram](t.me/openzeppelin_tg/2) to get help finding reviewers. + +## Best practices + +- Write clear and meaningful git commit messages. +- If the PR will _completely_ fix a specific issue, include `fixes #123` in the PR body (where 123 is the specific issue number the PR will fix. This will automatically close the issue when the PR is merged. +- Make sure you don't include `@mentions` or `fixes` keywords in your git commit messages. These should be included in the PR body instead. +- When you make a PR for small change (such as fixing a typo, style change, or grammar fix), please squash your commits so that we can maintain a cleaner git history. +- Make sure you include a clear and detailed PR description explaining the reasons for the changes, and ensuring there is sufficient information for the reviewer to understand your PR. +- Additional Readings: + - [chris.beams.io/posts/git-commit/](https://chris.beams.io/posts/git-commit/) + - [github.com/blog/1506-closing-issues-via-pull-requests](https://github.com/blog/1506-closing-issues-via-pull-requests) + - [davidwalsh.name/squash-commits-git](https://davidwalsh.name/squash-commits-git) + - [https://mtlynch.io/code-review-love/](https://mtlynch.io/code-review-love/) + +## Coding Standards + +- Use **Rust 2021 edition**, version `1.88` or later. +- Follow the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/). +- Run pre-commit hooks on your code to ensure code quality. + +## Testing + +Testing is the responsibility of all contributors as such all contributions must pass existing tests and include new tests when applicable: + +1. Write tests for new features or bug fixes. +2. Run the test suite: + + ```sh + cargo test + ``` + +3. Ensure no warnings or errors. +4. Make sure you have test coverage for your code. You can run ```RUST_TEST_THREADS=1 cargo llvm-cov --locked --html --open``` to open the coverage report in the browser and verify the percentages for your code. Make sure to have a minimum of 80% coverage. + +## Security + +- Follow the stated [Security Policy](SECURITY.md). + +## Documentation + +- 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 on [docs](https://github.com/OpenZeppelin/docs) repo for the content that is modified. + +- To generate technical rust documentation locally, run the following command + + ```sh + cargo make rust-docs + ``` + +- Rust docs will be generated in `docs/build/site/openzeppelin_relayer/` directory. + + +## Issue and Pull Request Labeling Guidelines + +To ensure clarity and effective project management, we use a structured labeling system for issues and pull requests. Below are the label categories and their purposes: + +### 1. Area Labels (`A-`) + +These labels identify the part of the project the issue or PR pertains to: + +**`A-arch`**: High-level architectural concerns or changes. +**`A-clients`**: Issues related to blockchain clients (e.g., EVM, Solana, Stellar). +**`A-pipeline`**: Signer, Provider, and global Relayer services and CI pipelines. +**`A-configs`**: Issues related to `.env` files, relayer configuration, or network settings. +**`A-tests`**: Test setup and integration. +**`A-docs`**: Updates or fixes to project documentation. +**`A-deps`**: Pull requests that update a dependency file. + +--- + +### 2. Type Labels (`T-`) + +These labels describe the nature of the issue or PR: + +**`T-bug`**: Indicates a bug report. +**`T-feature`**: Suggests a new feature or enhancement. +**`T-task`**: General tasks or chores (e.g., refactoring, cleanup). +**`T-documentation`**: Issues or PRs related to documentation updates. +**`T-performance`**: Performance optimizations or bottlenecks. +**`T-security`**: Security vulnerabilities or related fixes. + +--- + +### 3. Priority Labels (`P-`) + +Define the priority level for addressing issues: + +**`P-high`**: Critical tasks or blockers. +**`P-medium`**: Important but not urgent. +**`P-low`**: Low-priority or non-urgent tasks. + +--- + +### 4. Status Labels (`S-`) + +Labels to track the workflow status of an issue: + +**`S-needs-triage`**: Requires initial triage or categorization. +**`S-in-progress`**: Actively being worked on. +**`S-blocked`**: Blocked by another issue or dependency. +**`S-needs-review`**: Awaiting review (code or design). +**`S-closed`**: Completed and closed issues. + +--- + +### 5. Difficulty Labels (`D-`) + +Indicate the complexity or effort required to address the issue: + +**`D-easy`**: Beginner-friendly tasks. +**`D-medium`**: Intermediate-level tasks. +**`D-hard`**: Complex or advanced issues. + +--- + +### 6. Other Useful Labels + +**`good-first-issue`**: Beginner-friendly, low-complexity issues to help new contributors. +**`help-wanted`**: Issues where community contributions are welcome. +**`discussion`**: Requires community or team input. +**`wontfix`**: This will not be worked on. +**`duplicate`**: This issue or pull request already exists. + +--- + +### How to Use These Labels + +When creating or triaging an issue or PR, apply the appropriate labels from the categories above. This helps maintain clarity, improve collaboration, and ensure smooth workflow management for all contributors. + +If you are unsure which label to apply, feel free to leave the issue or PR with the **`S-needs-triage`** label, and a maintainer will review it. + +## License + +By contributing to this project, you agree that your contributions will be licensed under the [AGPL-3.0 License](LICENSE). + +## Code of Conduct + +This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report any unacceptable behavior on [Telegram](t.me/openzeppelin_tg/2).` diff --git a/DOCKER_README.md b/DOCKER_README.md new file mode 100644 index 000000000..797ee000b --- /dev/null +++ b/DOCKER_README.md @@ -0,0 +1,90 @@ +# OpenZeppelin Relayer + +This relayer service enables interaction with blockchain networks through transaction submissions. It offers multi-chain support and an extensible architecture for adding new chains. + +[User Docs](https://docs.openzeppelin.com/relayer/) | [Quickstart](https://docs.openzeppelin.com/relayer/quickstart) + +## Pre-requisites + +- Docker installed on your machine +- [Sodium](https://doc.libsodium.org/). See [install sodium section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#install-sodium) for more information. +- `.env` file with the required environment variables & `config/config.json` file with the required configuration. See how to set it up in [config files section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#config-files) for more information. +- Create signers and add them to the `config/config.json` file. See how to set it up in [signers section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#creating-a-signer) for more information. +- Configure webook url in `config/config.json` file. See how to set it up in [webhook section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#configure-webhook-url) for more information. +- Configure webhook signing key in `config/config.json` file. See how to set it up in [webhook section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#configure-webhook-signing-key) for more information. +- Configure Api key in `config/config.json` file. See how to set it up in [api key section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#configure-api-key) for more information. +- Redis server running. See how to set it up in [redis section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#starting-redis-manually-without-docker-compose) for more information. + +> ⚠️ Redis is automatically started when using docker compose. If you are not using docker compose, you need to create a dedicated network and start redis manually. + +## How to use images pushed to DockerHub + +- These images are automatically pulled when you use docker compose. See [using docker compose](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#running-services-with-docker-compose) for more information. +- If you are not using docker compose and you want to use these images, follow the steps below. + +### 1. Pull the image + +You can pull the latest image using the following command: + +```bash +docker pull openzeppelin/openzeppelin-relayer:latest +``` + +### 2. Run the image + +You can run the image using the following command: + +```bash +docker run --env-file .env -d \ + --name relayer \ + --network relayer-net \ + -p 8080:8080 \ + -v ./config:/app/config:ro \ + openzeppelin/openzeppelin-relayer:latest +``` + +### 3. Access the service + +Once the container is running, you can access the service at `http://localhost:8080`. + +You can test the relayer by sending a request using a curl call. See [testing relayer section](https://github.com/OpenZeppelin/openzeppelin-relayer?tab=readme-ov-file#test-the-relayer) for more information. + +### 4. Stop the container + +You can stop the container using the following command: + +```bash +docker stop relayer +``` + +### 5. Remove the container + +You can remove the container using the following command: + +```bash +docker rm relayer +``` + +### 6. Remove the image + +You can remove the image using the following command: + +```bash +docker rmi openzeppelin/openzeppelin-relayer:latest +``` + +## Contributing + +We welcome contributions to the OpenZeppelin Relayer. Please read our [contributing section](https://github.com/OpenZeppelin/openzeppelin-relayer/?tab=readme-ov-file#contributing) for more information. + +## Observability + +See the [observability section](https://github.com/OpenZeppelin/openzeppelin-relayer/?tab=readme-ov-file#observability) for more information on how to set up observability for the relayer. + +## License + +This project is licensed under the GNU Affero General Public License v3.0 - see the [LICENSE](https://github.com/OpenZeppelin/openzeppelin-relayer/blob/main/LICENSE) file for details. + +## Security + +For security concerns, please refer to our [Security Policy](https://github.com/OpenZeppelin/openzeppelin-relayer/blob/main/SECURITY.md). diff --git a/Dockerfile.integration b/Dockerfile.integration new file mode 100644 index 000000000..937f39385 --- /dev/null +++ b/Dockerfile.integration @@ -0,0 +1,98 @@ +# Multi-stage Dockerfile for Integration Tests +# Uses Chainguard images for security and minimal footprint + +# ============================================================================ +# Builder Stage - Compile integration tests +# ============================================================================ +# Rustc v1.91.1 +# The digest needs to be updated everytime the minimum version is changed, and if the version is above 1.91.1 +FROM cgr.dev/chainguard/rust:latest-dev@sha256:33faad9a26e8437ed9725bea3eb2d1e85facd1c035a31af8d485ea8c0a935532 AS builder + +USER root +RUN apk update && apk --no-cache add \ + openssl-dev \ + perl \ + libsodium-dev + +WORKDIR /app + +# Install llvm-tools-preview component and cargo-llvm-cov for coverage +RUN rustup default stable && \ + rustup component add llvm-tools-preview && \ + cargo install cargo-llvm-cov --locked + +# Copy manifests +COPY Cargo.toml Cargo.lock ./ + +# Fetch dependencies (this layer will be cached) +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/git \ + cargo fetch --locked + +# Copy actual source +COPY src ./src +COPY tests ./tests + +# Build integration tests with coverage instrumentation +# Using RUSTFLAGS to enable coverage instrumentation during compilation +# This avoids the triple compilation issue (normal build, test build, coverage build) +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/git \ + --mount=type=cache,target=/app/target \ + RUSTFLAGS="-C instrument-coverage" \ + cargo test --features integration-tests --test integration --release --no-run && \ + TEST_BINARY=$(find target/release/deps -name 'integration-*' -type f -executable | head -n 1) && \ + cp "$TEST_BINARY" /app/integration-test-binary + +# ============================================================================ +# Runtime Stage - Minimal image with only what's needed to run tests and generate coverage +# ============================================================================ +FROM cgr.dev/chainguard/wolfi-base AS runtime + +WORKDIR /app + +# Copy LLVM tools and libraries from builder (architecture-agnostic with wildcards) +# This fixes the hardcoded aarch64 issue - works on both x86_64 and aarch64 +COPY --from=builder /root/.rustup/toolchains/stable-*/lib/rustlib/*/bin/llvm-cov \ + /usr/local/bin/llvm-cov +COPY --from=builder /root/.rustup/toolchains/stable-*/lib/rustlib/*/bin/llvm-profdata \ + /usr/local/bin/llvm-profdata + +# Copy LLVM shared libraries that llvm-cov and llvm-profdata depend on +# Use a broad pattern to cover libLLVM.so.* and libLLVM-*.so variants. +COPY --from=builder /root/.rustup/toolchains/stable-*/lib/libLLVM*.so* /usr/local/lib/ +COPY --from=builder /root/.rustup/toolchains/stable-*/lib/librustc_driver-*.so /usr/local/lib/ + +# Update library path so the LLVM tools can find their dependencies +ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH + +# Copy instrumented test binary from builder +COPY --from=builder /app/integration-test-binary ./integration-tests + +# Copy SSL libraries from builder +COPY --from=builder /usr/lib/libssl.so.3 /usr/lib/libssl.so.3 +COPY --from=builder /usr/lib/libcrypto.so.3 /usr/lib/libcrypto.so.3 + +# Copy test data files (contracts, etc.) +COPY tests/integration ./tests/integration + +# Create config and networks directory structure (will be mounted via docker-compose) +# Networks is mounted separately at /app/networks to avoid nested mount issues +RUN mkdir -p /app/config /app/networks + +# Create directories for coverage output with proper permissions +RUN mkdir -p /app/coverage /app/profraw && chmod -R 777 /app/coverage /app/profraw + +# Execute tests directly, then generate lcov report using llvm-cov +# This approach: +# 1. Runs the instrumented test binary (generates .profraw files) +# 2. Merges .profraw files into .profdata +# 3. Generates lcov report from .profdata +# All without invoking cargo or recompiling +CMD ["sh", "-c", "\ + LLVM_PROFILE_FILE=/app/profraw/integration-%p-%m.profraw \ + ./integration-tests --nocapture --test-threads=1 && \ + llvm-profdata merge -sparse /app/profraw/*.profraw -o /app/coverage/integration.profdata && \ + llvm-cov export --format=lcov \ + --instr-profile=/app/coverage/integration.profdata \ + ./integration-tests > /app/coverage/integration-lcov.info"] diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..ef63a8330 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,32 @@ +# Security Policy + +Security vulnerabilities should be [disclosed](#reporting-a-vulnerability) to the [project maintainers](./CODEOWNERS), or alternatively by email to security@openzeppelin.com. + +## Supported Versions + +The following versions are currently supported and receive security updates. Alpha, Beta and Release candidates will not receive security updates. + +Security patches will be released for the latest minor of a given major release. For example, if an issue is found in versions >=1.13.0 and the latest is 1.14.0, the patch will be released only in version 1.14.1. + +Only critical severity bug fixes will be backported to past major releases. + +| Version | Supported | +| --------- | ------------------ | +| >= 0.1.x | :white_check_mark: | +| <= 0.0.9 | :x: | + +## Reporting a Vulnerability + +We're extremely grateful for security researchers and users that report vulnerabilities to us. +All reports are thoroughly investigated by the project's security team. + +Vulnerabilities are reported privately via GitHub's [Security Advisories](https://docs.github.com/en/code-security/security-advisories) feature. +Please use the following link to submit your vulnerability: [Report a vulnerability](https://github.com/openzeppelin/openzeppelin-relayer/security/advisories/new) + +Please see +[Privately reporting a security vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability) +for more information on how to submit a vulnerability using GitHub's interface. + +## Legal + +OpenZeppelin Relayer is made available under the GNU AGPL 3.0 License, which disclaims all warranties in relation to the project and which limits the liability of those that contribute and maintain the project, including OpenZeppelin. Your use of the project is also governed by the terms found at www.openzeppelin.com/tos (the "Terms"). As set out in the Terms, you are solely responsible for any use of OpenZeppelin Relayer and you assume all risks associated with any such use. This Security Policy in no way evidences or represents an on-going duty by any contributor, including OpenZeppelin, to correct any flaws or alert you to all or any of the potential risks of utilizing the project. diff --git a/docker-compose.integration.yml b/docker-compose.integration.yml new file mode 100644 index 000000000..745a688f7 --- /dev/null +++ b/docker-compose.integration.yml @@ -0,0 +1,116 @@ +--- +services: + redis: + image: redis:7-alpine + container_name: integration-redis + command: + - redis-server + networks: + - integration + healthcheck: + test: + - CMD + - redis-cli + - ping + interval: 3s + timeout: 2s + retries: 5 + start_period: 5s + anvil: + profiles: + - local + image: ghcr.io/foundry-rs/foundry:stable + container_name: integration-anvil + entrypoint: '' + command: + - sh + - -c + - anvil --host 0.0.0.0 --chain-id 31337 --block-time 1 + ports: + - 0.0.0.0:8545:8545 + networks: + - integration + volumes: + - ./tests/integration/contracts:/contracts:ro + healthcheck: + test: + - CMD + - cast + - client + - --rpc-url + - http://localhost:8545 + interval: 2s + timeout: 2s + retries: 10 + start_period: 3s + relayer: + build: + context: . + dockerfile: Dockerfile.production + container_name: integration-relayer + depends_on: + redis: + condition: service_healthy + anvil: + condition: service_healthy + required: false # Only when local profile is active, but we handle startup order in script + ports: + - 8080:8080 + networks: + - integration + env_file: + - .env.integration + environment: + - REDIS_URL=redis://redis:6379 + - IN_DOCKER=true + # Reduce log verbosity during tests - only show WARN and ERROR + # This filters out verbose OpenTelemetry traces + - RUST_LOG=warn,openzeppelin_relayer=warn,actix_web=warn,tracing_actix_web=error + volumes: + - ${CONFIG_SOURCE:-./tests/integration/config/local}:/app/config:ro + - ./config/networks:/app/networks:ro + healthcheck: + test: + - CMD + - node + - -e + - "require('http').get({hostname:'localhost',port:8080,path:'/api/v1/health',headers:{'Authorization':'Bearer '+process.env.API_KEY}}, r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))" + interval: 5s + timeout: 3s + retries: 15 + start_period: 10s + integration-tests: + image: openzeppelin-relayer-integration:latest + build: + context: . + dockerfile: Dockerfile.integration + target: runtime # Use runtime stage that executes pre-built binary with coverage + container_name: integration-tests + user: ${HOST_UID:-1000}:${HOST_GID:-1000} # Run as host user for proper file permissions + depends_on: + relayer: + condition: service_healthy + networks: + - integration + env_file: + - .env.integration + environment: + - RELAYER_BASE_URL=http://relayer:8080 + # Test logging configuration + # Default to INFO level for clean output, can be overridden with RUST_LOG + - RUST_LOG=${RUST_LOG:-info} + # Test registry path (tests discover relayers via API) + - TEST_REGISTRY_PATH=${TEST_REGISTRY_PATH:-tests/integration/config/registry.json} + volumes: + - ./tests/integration/config:/app/config:ro + - ./config/networks:/app/networks:ro + # Mount coverage output directory + - ./coverage:/app/coverage + # Mount profraw directory for debugging raw coverage data + - ./profraw:/app/profraw +networks: + integration: + driver: bridge + name: openzeppelin-integration +volumes: + test-results: From 1f9b6a2dcbb152b5a2032e9be553ecf8884af930 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Fri, 20 Mar 2026 12:44:51 +0100 Subject: [PATCH 10/10] restore 1 more missing file --- config/config.example.json | 105 +++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 config/config.example.json diff --git a/config/config.example.json b/config/config.example.json new file mode 100644 index 000000000..3600bb3bb --- /dev/null +++ b/config/config.example.json @@ -0,0 +1,105 @@ +{ + "relayers": [ + { + "id": "sepolia-example", + "name": "Sepolia Example", + "network": "sepolia", + "paused": false, + "notification_id": "notification-example", + "signer_id": "local-signer", + "network_type": "evm", + "policies": { + "min_balance": 0 + } + }, + { + "id": "stellar-example", + "name": "Stellar Example", + "network": "testnet", + "paused": false, + "notification_id": "notification-example", + "signer_id": "local-signer", + "network_type": "stellar" + }, + { + "id": "solana-example", + "name": "Solana Example", + "network": "devnet", + "paused": false, + "notification_id": "notification-example", + "signer_id": "local-signer", + "network_type": "solana", + "policies": { + "fee_payment_strategy": "user", + "min_balance": 0, + "swap_config": { + "strategy": "jupiter-swap", + "cron_schedule": "0 0 * * *", + "min_balance_threshold": 0 + }, + "allowed_programs": ["11111111111111111111111111111111", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"], + "allowed_tokens": [ + { + "mint": "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr", + "max_allowed_fee": 100000000, + "swap_config": { + "min_amount": 0, + "max_amount": 0, + "retain_min_amount": 0 + } + }, + { + "mint": "So11111111111111111111111111111111111111112" + } + ] + } + }, + { + "id": "solana-mainnet-example", + "name": "Solana Mainnet Example", + "network": "mainnet-beta", + "paused": false, + "notification_id": "notification-example", + "signer_id": "local-signer", + "network_type": "solana", + "policies": { + "min_balance": 0, + "allowed_tokens": [ + { + "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "max_allowed_fee": 100000000 + }, + { + "mint": "So11111111111111111111111111111111111111112" + } + ] + } + } + ], + "notifications": [ + { + "id": "notification-example", + "type": "webhook", + "url": "", + "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" + } + } + } + ], + "networks": "./config/networks", + "plugins": [] +}