Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Relayer

dataprism-relayer is a continuously running Node.js service that submits user meta-transactions on-chain. It polls the API for pending requests, checks each one for profitability, submits it to DataPrismCloudForwarder, and reports the result back to the API.

Tech stack

  • Language: TypeScript 5.8 · Runtime: Node.js
  • Key dependencies: viem 2.28 (chain interaction), nodemailer (email alerts), tslog (logging), pm2 (process management)

Project structure

src/
├── relayer/
│   ├── runner.ts          # Entry point: init executor + notifier, start the loop
│   ├── poller.ts          # Main polling loop
│   ├── executor.ts        # Simulates and submits transactions (retries, gas bumps)
│   ├── profitability.ts   # Profitability check via Uniswap V4 price
│   └── gas.ts             # Standalone ETH balance monitor
├── api/client.ts          # API client (auth + request management)
├── config.ts              # Environment variable parsing
├── notification.ts        # Email + Telegram notifications
└── abi/DataPrismCloudForwarder.ts

Polling loop (poller.ts)

Every POLLING_INTERVAL ms (default 5 s):

  1. Fetch pending requests via getPendingRequests() (GET /requests?status=pending).
  2. For each request:
    • Atomic claim (PATCH /requests/{id}processing), skip if already taken.
    • Profitability check: skip if not profitable.
    • Execution via executor.execute(req).
    • Report success (with txHash) or failure (with the error message) to the API.

Errors are caught per-request, so one failed request never halts the loop.

Profitability check (profitability.ts)

Optional, disabled when UNISWAP_POOL_MANAGER is empty. It compares the relayerFee (in DPC) against the estimated gas cost:

gasCostInFeeToken = estimateGas × maxFeePerGas × ETH_price_in_DPC
profitable        = relayerFee ≥ gasCostInFeeToken

The ETH/DPC price is read from a configurable Uniswap V4 pool (getSlot0sqrtPriceX96), via a separate price RPC endpoint (PRICE_RPC_URL_0).

On-chain execution (executor.ts)

Calls the *For functions on DataPrismCloudForwarder via viem. For each request:

  1. Simulation (simulateContract), a failure throws a non-retriable SimulationError (the contract would revert).
  2. Submission (writeContract).
  3. Wait for CONFIRMATIONS confirmations (with a timeout).

On a network failure (not a simulation error), the relayer retries with progressively higher gas (INCREASE_GAS_PERCENT × attempt) up to MAX_RETRIES, pausing if fees exceed MAX_FEE_PER_GAS. Multiple RPCs are supported in fallback mode (RPC_URL_0, RPC_URL_1).

Supported actions: CREATE_PRISM_FOR, WRITE_DATA_FOR, MODIFY_DATA_FOR, DELETE_DATA_FOR, TRANSFER_PRISM_FOR, ACCEPT_TRANSFER_FOR, DELETE_PRISM_FOR, BATCH_WRITE_DATA_FOR, BATCH_MODIFY_DATA_FOR, BATCH_DELETE_DATA_FOR, BATCH_MULTI_USER_WRITE_DATA.

For CREATE_PRISM_FOR the executor forwards the request's permit field as the PermitParams tuple (deadline, v, r, s) argument of createPrismFor (a zero tuple when the prism is unfunded), so the Forwarder can run token.permit(...) and fund the prism with no prior approve.

Gas monitor (gas.ts)

A separate PM2 process checks the relayer's ETH balance every GAS_POLLING_INTERVAL ms (default 3 min) and sends a notification when it drops below GAS_MIN_BALANCE (default 1 ETH).

Configuration

The relayer reads a .env file. The account must be whitelisted in the API before it can relay.

# Identity & contracts
RELAYER_PRIVATE_KEY=""
DATAPRISM_FORWARDER_ADDRESS=""   # DataPrismCloudForwarder
 
# API
API_BASE_URL=""
CHAIN_NAME=""                    # optional, filters requests by chain
 
# RPC (with fallback)
RPC_URL_0=""
RPC_URL_1=""
 
# Polling & gas
POLLING_INTERVAL="5000"
MAX_FEE_PER_GAS="100000000000"          # 100 Gwei
MAX_PRIORITY_FEE_PER_GAS="100000000000"
INCREASE_GAS_PERCENT="20"               # gas bump % per retry
MAX_RETRIES="50"
RETRY_DELAY="5000"
CONFIRMATIONS="2"
CONFIRMATIONS_TIMEOUT="30000"
GAS_MIN_BALANCE="1000000000000000000"   # 1 ETH (triggers alert)
GAS_POLLING_INTERVAL="180000"           # balance check every 3 min
 
# Profitability via Uniswap V4 (leave empty to disable)
UNISWAP_POOL_MANAGER=""
UNISWAP_CURRENCY0="0x0000000000000000000000000000000000000000" # ETH
UNISWAP_CURRENCY1="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" # USDC (mainnet)
UNISWAP_FEE="500"
UNISWAP_TICK_SPACING="10"
UNISWAP_HOOKS="0x0000000000000000000000000000000000000000"
PRICE_RPC_URL_0=""              # mainnet RPC for price reading
PRICE_RPC_URL_1=""
 
# Notifications (optional)
SMTP_HOST="" SMTP_PORT="465" SMTP_USER="" SMTP_PASS="" SMTP_FROM="" SMTP_TO=""
TELEGRAM_BOT_TOKEN="" TELEGRAM_CHAT_ID=""

Install, run, deploy

npm install        # Install dependencies
npm run typecheck  # tsc --noEmit
npm run dev        # Build + run runner.js directly
npm run build      # Compile to dist/
npm run deploy     # Build + start relayer & gas monitor via PM2
npm run list       # pm2 list

PM2 (ecosystem.config.js) runs two processes: DataPrism Relayer (runner.js) and DataPrism Gas Monitor (gas.js). Deploy multiple instances (each with its own key) for redundancy, the atomic claim makes this safe.

API authentication

The relayer authenticates to the API with an ECDSA signature over the request headers (X-Relayer-Address, X-Relayer-Signature, X-Relayer-Timestamp). See API → Authentication for the message format.

Copyright © 2026 DataPrism.