Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,853 changes: 3,606 additions & 247 deletions Cargo.lock

Large diffs are not rendered by default.

136 changes: 79 additions & 57 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
name = "openzeppelin-relayer"
version = "1.1.0"
edition = "2021"
rust-version = "1.86" #MSRV
rust-version = "1.86" #MSRV
default-run = "openzeppelin-relayer"

[profile.release]
opt-level = 0
Expand All @@ -15,88 +16,105 @@ opt-level = 0
overflow-checks = true
panic = "unwind"

[patch."https://github.com/input-output-hk/midnight-ledger-prototype"]
mn-ledger-storage = { git = "https://github.com/midnightntwrk/midnight-ledger-prototype", package = "midnight-storage", tag = "ledger-4.0.0" }
coin-structure = { git = "https://github.com/midnightntwrk/midnight-ledger-prototype", package = "midnight-coin-structure", tag = "ledger-4.0.0" }
onchain-runtime = { git = "https://github.com/midnightntwrk/midnight-ledger-prototype", package = "midnight-onchain-runtime", tag = "ledger-4.0.0" }
midnight-serialize = { git = "https://github.com/midnightntwrk/midnight-ledger-prototype", tag = "ledger-4.0.0" }
base-crypto = { git = "https://github.com/midnightntwrk/midnight-ledger-prototype", package = "midnight-base-crypto", tag = "ledger-4.0.0" }
transient-crypto = { git = "https://github.com/midnightntwrk/midnight-ledger-prototype", package = "midnight-transient-crypto", tag = "ledger-4.0.0" }

[dependencies]
actix-governor = "0.8"
actix-rt = "2.0.0"
actix-web = "4"
aes-gcm = "0.10"
alloy = { version = "0.9", features = ["full"] }
apalis = { version = "0.7", features = ["limit", "retry", "catch-panic", "timeout"] }
apalis-cron = { version = "0.7" }
apalis-redis = { version = "0.7" }
async-trait = "0.1"
aws-config = { version = "1.6.3", features = ["behavior-version-latest"] }
aws-sdk-kms = "1.75.0"
actix-web = "4"
log = "0.4"
simplelog = "0.12"
prometheus = "0.14"
lazy_static = "1.5"
backoff = { version = "0.4.0", features = ["tokio"] }
base64 = { version = "0.22" }
bech32 = "0.11.0"
bincode = { version = "1.3.3" }
bs58 = "0.5"
bytes = { version = "1.9" }
chrono = "0.4"
color-eyre = "0.6"
dashmap = { version = "6.1" }
dotenvy = "0.15"
thiserror = "2"
async-trait = "0.1"
actix-rt = "2.0.0"
alloy = { version = "0.9", features = ["full"] }
serde_json = "1"
ed25519-dalek = "2.2"
eyre = "0.6"
futures = "0.3"
futures-util = "0.3"
google-cloud-auth = "0.22.1"
governor = "0.8.0"
hex = { version = "0.4" }
hmac = { version = "0.12" }
http = { version = "1.3.1" }
itertools = "0.12.0" # Required by midnight
json-patch = "4.0"
strum = { version = "0.27", default-features = false, features = ["derive"] }
strum_macros = "0.27"
serde = { version = "1.0", features = ["derive", "alloc"] }
k256 = { version = "0.13", features = ["ecdsa-core"] }
lazy_static = "1.5"
libsodium-sys = "0.2.7"
log = "0.4"
midnight-ledger-prototype = { git = "https://github.com/midnightntwrk/midnight-ledger-prototype", package = "midnight-ledger", tag = "ledger-4.0.0" }
midnight-node-ledger-helpers = { git = "https://github.com/midnightntwrk/midnight-node", package = "midnight-node-ledger-helpers", tag = "node-0.12.0" }
midnight-node-res = { git = "https://github.com/midnightntwrk/midnight-node", package = "midnight-node-res", tag = "node-0.12.0" }
mpl-token-metadata = { version = "5.1" }
num_enum = { version = "0.7", default-features = false }
once_cell = "1.17"
regex = "1"
futures = "0.3"
uuid = { version = "1.11", features = ["v4"] }
chrono = "0.4"
eyre = "0.6"
color-eyre = "0.6"
apalis = { version = "0.7", features = ["limit", "retry", "catch-panic", "timeout"] }
apalis-redis = { version = "0.7" }
apalis-cron = { version = "0.7" }
redis = { version = "0.32", features = ["aio", "connection-manager", "tokio-comp"] }
tokio = { version = "1.43", features = ["sync", "io-util", "time"] }
rand = "0.9"
oz-keystore = { version = "0.1.4" }
p256 = { version = "0.13.2" }
parking_lot = "0.12"
tower = "0.5"
oz-keystore = { version = "0.1.4"}
hex = { version = "0.4"}
bytes = { version = "1.9" }
pem = { version = "3" }
prometheus = "0.14"
rand = "0.9"
redis = { version = "0.32", features = ["aio", "connection-manager", "tokio-comp"] }
regex = "1"
reqwest = { version = "0.12", features = ["json"] }
base64 = { version = "0.22" }
hmac = { version = "0.12" }
secrets = { version = "1.2" }
serde = { version = "1.0", features = ["derive", "alloc"] }
serde_json = "1"
sha2 = { version = "0.10" }
sha3 = { version = "0.10" }
dashmap = { version = "6.1" }
actix-governor = "0.8"
solana-sdk = { version = "2.2" }
simple_asn1 = { version = "0.6" }
simplelog = "0.12"
solana-client = { version = "2.2" }
solana-sdk = { version = "2.2" }
solana-system-interface = { version = "1.0.0", features = ["bincode"] }
soroban-rs = "0.2.5"
spl-associated-token-account = "6.0.0"
spl-token = { version = "8" }
spl-token-2022 = { version = "8" }
mpl-token-metadata = { version = "5.1" }
stellar-strkey = "0.0.13"
strum = { version = "0.27", default-features = false, features = ["derive"] }
strum_macros = "0.27"
subtle = "2.6"
subxt = { version = "0.37.0", features = ["substrate-compat"] }
sysinfo = "0.36"
bincode = { version = "1.3" }
bs58 = "0.5"
spl-associated-token-account = "6.0.0"
itertools = "0.14.0"
thiserror = "2"
tokio = { version = "1.43", features = ["sync", "io-util", "time"] }
tokio-tungstenite = { version = "0.20", features = ["native-tls"] }
tower = "0.5"
utoipa = { version = "5.3", features = ["actix_extras"] }
uuid = { version = "1.11", features = ["v4"] }
validator = { version = "0.20", features = ["derive"] }
vaultrs = { version = "0.7.4" }
utoipa = { version = "5.3", features = ["actix_extras"] }
secrets = { version = "1.2"}
libsodium-sys = "0.2.7"
zeroize = "1.8"
subtle = "2.6"
aes-gcm = "0.10"
ed25519-dalek = "2.2"
stellar-strkey = "0.0.13"
soroban-rs = "0.2.5"
p256 = { version = "0.13.2" }
google-cloud-auth = "0.22.1"
http = { version = "1.3.1" }
pem = { version = "3" }
simple_asn1 = { version = "0.6" }
k256 = { version = "0.13", features = ["ecdsa-core"]}
solana-system-interface = { version = "1.0.0", features = ["bincode"] }

[dev-dependencies]
cargo-llvm-cov = "0.6"
clap = { version = "4.4", features = ["derive"] }
mockall = { version = "0.13" }
mockito = "1.6.1"
proptest = "1.6.0"
rand = "0.9.0"
tempfile = "3.2"
serial_test = "3.2"
clap = { version = "4.4", features = ["derive"] }
tempfile = "3.2"
wiremock = "0.6"

[[bin]]
Expand All @@ -105,6 +123,10 @@ path = "src/main.rs"
doc = true
doctest = true

[[example]]
name = "generate_midnight_fixtures"
path = "scripts/fixtures/generate_midnight_fixtures.rs"

[[example]]
name = "test_tx"
path = "helpers/test_tx.rs"
Expand Down
93 changes: 93 additions & 0 deletions Dockerfile.midnight.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Base image
FROM --platform=${BUILDPLATFORM} cgr.dev/chainguard/rust:latest-dev@sha256:faf49718aaa95c798ed1dfdf3e4edee2cdbc3790c8994705ca6ef35972128459 AS base

USER root
RUN apk update && apk --no-cache add \
openssl-dev \
perl \
libsodium-dev \
git \
curl

ENV PKG_CONFIG_PATH=/usr/lib/pkgconfig

WORKDIR /usr/app

# Configure git to use credentials for private repos
# TEMPORARY: GitHub credentials are only required until Midnight repositories are open-sourced
# Once the repos are public, this authentication step can be removed entirely
# Credentials are provided via Docker secrets for security (not exposed in logs)
RUN --mount=type=secret,id=github_user,mode=0400 \
--mount=type=secret,id=github_pat,mode=0400 \
if [ ! -f /run/secrets/github_user ] || [ ! -f /run/secrets/github_pat ]; then \
echo "ERROR: GitHub credentials must be provided as Docker secrets" && \
echo "Please ensure GITHUB_USER and GITHUB_PAT are set in your .env file" && \
echo "Note: This is temporary until Midnight repositories are open-sourced" && \
exit 1; \
fi && \
GITHUB_USER=$(cat /run/secrets/github_user) && \
GITHUB_PAT=$(cat /run/secrets/github_pat) && \
echo "Configuring git for user: ${GITHUB_USER}" && \
echo "Note: GitHub authentication is temporary until Midnight repos are public" && \
mkdir -p /root/.cargo && \
echo '[net]' >> /root/.cargo/config.toml && \
echo 'git-fetch-with-cli = true' >> /root/.cargo/config.toml && \
git config --global url."https://${GITHUB_USER}:${GITHUB_PAT}@github.com/".insteadOf "https://github.com/" && \
echo "Git config set. Testing access..." && \
git ls-remote https://github.com/midnightntwrk/midnight-ledger-prototype > /dev/null 2>&1 && echo "✓ Git access successful" || echo "✗ Git access failed"

# Copy source
COPY . .

# Build
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/app/target \
--mount=type=secret,id=github_user,mode=0400 \
--mount=type=secret,id=github_pat,mode=0400 \
GITHUB_USER=$(cat /run/secrets/github_user) && \
GITHUB_PAT=$(cat /run/secrets/github_pat) && \
cargo build --release && \
cargo install --root /usr/app --path . --debug && \
git config --global --unset-all url."https://${GITHUB_USER}:${GITHUB_PAT}@github.com/".insteadOf && \
echo "Git credentials cleared"

# Setting up build directories
FROM --platform=${BUILDPLATFORM} cgr.dev/chainguard/wolfi-base

WORKDIR /app

COPY --from=base --chown=nonroot:nonroot /usr/app/bin/openzeppelin-relayer /app/openzeppelin-relayer

# Install plugin dependencies
ARG TARGETARCH
ARG NODE_VERSION=20.19

# Install Node.js
USER root
RUN apk add --no-cache nodejs=~${NODE_VERSION} npm

ENV PATH="/usr/local/bin:$PATH"

# Install pnpm and ts-node
RUN npm install -g pnpm ts-node typescript

# removes apk and unneeded wolfi-base tools.
RUN apk del wolfi-base apk-tools

# Copy plugins folder and install dependencies
COPY --chown=nonroot:nonroot ./plugins /app/plugins

USER nonroot
WORKDIR /app/plugins
RUN pnpm install --frozen-lockfile

# Return to app root
WORKDIR /app

ENV APP_PORT=8080
ENV METRICS_PORT=8081

EXPOSE ${APP_PORT}/tcp ${METRICS_PORT}/tcp

# starting up
ENTRYPOINT ["/app/openzeppelin-relayer"]
30 changes: 14 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
[![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)
Expand All @@ -33,6 +32,7 @@ This relayer service enables interaction with blockchain networks through transa
- Solana
- EVM
- Stellar
- Midnight (🚧 Partial support)

> For details about current development status and upcoming features, check our [Project Roadmap](https://docs.openzeppelin.com/relayer/roadmap).

Expand All @@ -50,21 +50,19 @@ View the [Usage](https://docs.openzeppelin.com/relayer#running_the_relayer) docu

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 |
| [`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 |
| [`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 |


| Example | Description |
| ------------------------------------------------------------------------------------ | ------------------------------------------------------- |
| [`basic-example`](./examples/basic-example/) | Simple setup with Redis |
| [`redis-storage`](./examples/redis-storage/) | Simple setup with Redis for storage |
| [`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 |
| [`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 |

Each example includes:

Expand Down
17 changes: 16 additions & 1 deletion config/config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@
"cron_schedule": "0 0 * * *",
"min_balance_threshold": 0
},
"allowed_programs": ["11111111111111111111111111111111", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
"allowed_programs": [
"11111111111111111111111111111111",
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
],
"allowed_tokens": [
{
"mint": "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr",
Expand Down Expand Up @@ -74,6 +77,18 @@
}
]
}
},
{
"id": "midnight-testnet-example",
"name": "Midnight Testnet Example",
"network": "testnet",
"paused": false,
"notification_id": "notification-example",
"signer_id": "local-signer",
"network_type": "midnight",
"policies": {
"min_balance": 0
}
}
],
"notifications": [
Expand Down
19 changes: 19 additions & 0 deletions config/networks/midnight.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"networks": [
{
"type": "midnight",
"network": "testnet",
"rpc_urls": ["wss://rpc.testnet-02.midnight.network/"],
"explorer_urls": [
"https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.testnet-02.midnight.network#/explorer"
],
"indexer_urls": {
"ws": "wss://indexer.testnet-02.midnight.network/api/v1/graphql/ws",
"http": "https://indexer.testnet-02.midnight.network/api/v1/graphql"
},
"prover_url": "http://localhost:6300",
"average_blocktime_ms": 6000,
"is_testnet": true
}
]
}
1 change: 1 addition & 0 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* xref:evm.adoc[EVM Integration]
* xref:solana.adoc[Solana Integration]
* xref:stellar.adoc[Stellar Integration]
* xref:midnight.adoc[Midnight Integration]
* link:https://release-v1-1-0%2D%2Dopenzeppelin-relayer.netlify.app/api_docs.html[API Reference^]
* xref:structure.adoc[Project Structure]
* xref:roadmap.adoc[Project Roadmap]
Expand Down
Loading
Loading