mirror of
https://github.com/0xbow-io/privacy-pools-core.git
synced 2026-01-09 17:37:58 -05:00
303 lines
9.9 KiB
Markdown
303 lines
9.9 KiB
Markdown
# 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 |
|
||
|
||
---
|