Files
privacy-pools-core/packages/relayer/README.md

303 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Privacy Pools Relayer
A relayer service that facilitates private withdrawals and fee quoting for Privacy Pools.
It provides an API for users or frontends to:
- Submit withdrawal requests
- Get real-time fee quotes (including gas cost, relayer margin, and swap fees)
- Retrieve relayer fee configuration details
Built with TypeScript, Express.js, and SQLite. Supports multi-chain configurations.
---
# Setup
## 1. Install Dependencies
```bash
yarn install
```
## 2. Environment Variables
You must set the following environment variables before running:
| Variable | Purpose |
| --------------------- | --------------------------------------------------- |
| `INFURA_API_KEY` | For accessing Sepolia RPC (and Uniswap quoting) |
| `RPC_URL` | RPC URL fallback if not configured in `config.json` |
| `RELAYER_PRIVATE_KEY` | Private key used to sign fee commitments |
| `SQLITE_DB_PATH` | (optional) Override path to SQLite DB |
*(You can create a `.env` file locally if you prefer.)*
## 3. Build and Start
```bash
yarn build:start
```
For TypeScript live mode:
```bash
yarn start:ts
```
Or using Docker:
```bash
yarn docker:build
yarn docker:run
```
---
# Configuration
### Important Considerations for Relayer Fee Setup
When configuring your relayer, keep the following in mind:
- In the config file, you define a `fee_bps` (basis points) value, e.g., **100 BPS** for **0.1%**.
- This represents the **desired profit margin** over the value withdrawn.
- During operation:
- The relayer estimates the **transaction cost** (`tx_cost`) for executing a relay (gas used × gas price).
- It calculates the **effective feeBPS** needed to fully cover `tx_cost` and still achieve the configured `fee_bps` margin.
- This "effective" fee is dynamically adjusted upwards if gas prices spike.
- If the value withdrawn is too small and the relayer cannot achieve at least the configured margin, the transaction will be **rejected**.
TLDR: fee_bps will be your profit over the withdrawn value. If the value is too small to cover the tx_fees and the relayer cut, it will reject the tx.
---
The relayer uses a `config.json` file to manage chains, assets, fees, and operational settings.
For setting up your config, use config.example.json as a basic template.
## Configuration Reference
The relayer is configured through a `config.json` file.
This file defines global defaults, per-chain settings, supported assets, and operational parameters.
Below is the complete description of all fields, their types, and behaviors.
---
### Top-Level Fields
| Field | Type | Required | Description |
|:------|:-----|:---------|:------------|
| `defaults` | Object | Yes | Default addresses and private keys for all chains unless overridden. |
| `chains` | Array of Objects | Yes | List of supported chains and their specific settings. |
| `sqlite_db_path` | String (absolute or relative path) | Yes | Path to the SQLite database file. |
| `cors_allow_all` | Boolean | Yes (default: `false`) | Whether to allow all CORS origins or restrict to allowed domains. |
| `allowed_domains` | Array of Strings (URLs) | Yes | List of allowed CORS domains if `cors_allow_all` is false. |
---
### `defaults` Object
| Field | Type | Required | Description |
|:------|:-----|:---------|:------------|
| `fee_receiver_address` | String (0x-prefixed address) | Yes | Address where relayer fees are collected. |
| `signer_private_key` | String (0x-prefixed private key) | Yes | Private key used to sign fee commitments. |
| `entrypoint_address` | String (0x-prefixed address) | Yes | Entrypoint contract address for relayer operations. |
---
### `chains` Array
Each entry represents a supported chain configuration.
| Field | Type | Required | Description |
|:------|:-----|:---------|:------------|
| `chain_id` | Number or String | Yes | Chain ID (e.g., 1 for Ethereum, 11155111 for Sepolia). |
| `chain_name` | String | Yes | Human-readable chain name. |
| `rpc_url` | String (URL) | Yes | JSON-RPC endpoint to connect to the chain. |
| `max_gas_price` | String or Number (wei) | Optional | Maximum gas price allowed for relaying (in wei). If exceeded, relayer rejects transaction. |
| `fee_receiver_address` | String (0x-prefixed address) | Optional | Chain-specific fee receiver. Overrides `defaults.fee_receiver_address` if set. |
| `signer_private_key` | String (0x-prefixed private key) | Optional | Chain-specific signer. Overrides `defaults.signer_private_key` if set. |
| `entrypoint_address` | String (0x-prefixed address) | Optional | Chain-specific entrypoint. Overrides `defaults.entrypoint_address` if set. |
| `native_currency` | Object | Optional | Info about the chain's native currency (ETH, MATIC, etc). |
| `supported_assets` | Array of Objects | Optional | List of ERC-20 or native assets supported for withdrawals on this chain. |
---
### `chains[].native_currency` Object
| Field | Type | Required | Description |
|:------|:-----|:---------|:------------|
| `name` | String | No (default: `"Ether"`) | Full name of the native currency. |
| `symbol` | String | No (default: `"ETH"`) | Ticker symbol for the native currency. |
| `decimals` | Number | No (default: `18`) | Number of decimals used by the native currency. |
---
### `chains[].supported_assets` Array
Each entry represents a supported ERC-20 token (or native token).
| Field | Type | Required | Description |
|:------|:-----|:---------|:------------|
| `asset_address` | String (0x-prefixed address) | Yes | Token contract address. Use `0xEeeee...` for native assets. |
| `asset_name` | String | Yes | Human-readable name of the token. |
| `fee_bps` | String or Number | Yes | Fee in basis points (1/100th of a percent). 100 = 1%. |
| `min_withdraw_amount` | String or Number | Yes | Minimum withdrawal amount allowed for this asset (in base units, not decimals). |
---
### Behavior and Fallbacks
- If `fee_receiver_address`, `signer_private_key`, or `entrypoint_address` are missing in a chain config, the relayer **falls back to `defaults`** and logs a warning.
- If `max_gas_price` is not set, no gas price limits are enforced (relayer accepts any gas price).
- If `supported_assets` is missing, no assets are available for withdrawal on that chain.
- If `native_currency` is missing, defaults to:
- Name: `"Ether"`
- Symbol: `"ETH"`
- Decimals: `18`
- If withdrawal or quote transactions cannot meet the configured profit margin (`fee_bps`), the request will be rejected.
---
# Notes
- All addresses must be valid EVM addresses (0x-prefixed, checksummed).
- All private keys must be valid hex strings starting with `0x`.
- Fee calculations automatically adjust based on gas costs and swap rates when quoting.
### Configuration Fields
- **fee_receiver_address**: Where relayer fees are collected.
- **signer_private_key**: Used to sign fee commitments.
- **entrypoint_address**: Contract address handling withdrawals.
- **chains**: Supported blockchain networks.
- **supported_assets**: Supported ERC-20 or native assets with:
- **fee_bps**: Relayer profit margin (in basis points).
- **min_withdraw_amount**: Minimum withdrawal size allowed.
---
# API Endpoints
## POST /relayer/quote
Returns a fee quote for a withdrawal, including dynamic feeBPS adjusted for gas costs.
### Request Body
```json
{
"chainId": 11155111,
"amount": "1000000000000000000",
"asset": "0xTokenAddress",
"recipient": "0xRecipientAddress"
}
```
### Response
```json
{
"baseFeeBPS": "200",
"feeBPS": "291",
"feeCommitment": {
"expiration": 1744676669549,
"withdrawalData": "0x...",
"signedRelayerCommitment": "0x..."
}
}
```
If `recipient` is provided, a signed relayer commitment is returned, valid for 20 seconds.
---
## POST /relayer/request
Submits a full withdrawal payload for execution, optionally using a previously signed fee commitment.
### Request Body
```json
{
"chainId": 11155111,
"scope": "0x123...",
"withdrawal": {
"processooor": "0x...",
"data": "0x..."
},
"proof": {
"pi_a": ["..."],
"pi_b": [["...", "..."], ["...", "..."]],
"pi_c": ["..."]
},
"publicSignals": [...],
"feeCommitment": {
"expiration": 1744676669549,
"withdrawalData": "0x...",
"signedRelayerCommitment": "0x..."
}
}
```
### Response
```json
{
"success": true,
"txHash": "0x...",
"timestamp": 1744676669549,
"requestId": "uuid"
}
```
If no `feeCommitment` is provided, the relayer checks if the feeBPS in the payload is high enough. Otherwise, it validates the signed commitment.
---
## GET /relayer/details
Returns configuration for a given chain and asset.
### Query Parameters
| Parameter | Required | Description |
| -------------- | -------- | ---------------------------- |
| `chainId` | Yes | Chain ID as number |
| `assetAddress` | Yes | Asset address (0x hex format) |
### Response
```json
{
"chainId": 11155111,
"feeBPS": "200",
"minWithdrawAmount": "1000",
"feeReceiverAddress": "0x...",
"assetAddress": "0x...",
"maxGasPrice": "2392000000"
}
```
---
# Available Scripts
| Script | Description |
| ---------------- | --------------------------------------------------------- |
| `build` | Compile code with tsc |
| `start` | Run the compiled server |
| `build:start` | Compile and run in one command |
| `start:ts` | Run using ts-node (dev mode) |
| `check-types` | Type-check the codebase |
| `lint` / `lint:fix` | Check or fix ESLint violations |
| `format` / `format:fix` | Check or fix Prettier formatting |
| `test` / `test:cov` | Run tests or generate coverage |
| `docker:build` / `docker:run` | Build or run the relayer using Docker |
---