Payment Relayer
dataprism-payment-relayer bridges fiat card payments to on-chain DPC tokens. After a user
completes a Stripe payment on the Frontend, this service buys DPC from
the Exchange with USDC and transfers it to the user's wallet.
Not to be confused with the Relayer: that service relays user data meta-transactions, while the payment relayer handles the fiat → DPC on-ramp.
Tech stack
- Language: TypeScript (ES2022) · Runtime: Node.js
- Key dependencies: viem
2.28, tslog, nodemailer, dotenv, pm2
Project structure
src/
├── relayer/
│ ├── runner.ts # Entry point: bootstrap executor + notifier, start the poller
│ ├── poller.ts # Polling loop: fetch, claim, execute, report
│ ├── executor.ts # buyAndTransfer(): buy DPC on the Exchange, transfer to user
│ └── gas.ts # ETH balance monitor (separate process)
├── api/client.ts # HTTP client for the frontend's relayer endpoints
├── abi/ # ERC20, Exchange, DPCToken ABIs
├── config.ts # Environment variable parsing
└── notification.ts # Email + Telegram alertsPayment flow
User pays via Stripe → Frontend records a pending payment
↓
Payment relayer polls GET /api/relayer/pending
↓
Claims the payment POST /api/relayer/claim (atomic, dedupes across instances)
↓
Buys DPC on the Exchange with USDC (1% slippage), then transfers DPC to the user
↓
Reports completion POST /api/relayer/complete { paymentIntentId, txHash }buyAndTransfer() (executor.ts)
- Acquire DPC: check the relayer's DPC balance. If insufficient, verify gas is below
MAX_FEE_GWEI, ensure (unlimited) USDC approval to the Exchange, then callExchange.buyTokens(USDC_ADDRESS, usdcAmount, minOut)with 1% slippage tolerance and wait for 1 confirmation. - Transfer DPC: call
ERC20.transfer(toAddress, dpcAmount)on the DPC token and wait for 2 confirmations; return the transfer tx hash.
Both steps run inside a retry loop (MAX_RETRIES, RETRY_DELAY_MS). The buy step is
idempotent: if the relayer already holds enough DPC from a prior failed attempt, it skips
straight to the transfer. DPC has 18 decimals, USDC has 6; the USDC amount to spend is derived
from the configured EXCHANGE_RATE (1 USDC → 1000 DPC by default).
Frontend endpoints consumed
The payment relayer is a client: it exposes no HTTP server. It calls three frontend
endpoints, each with an x-relayer-secret: <RELAYER_SECRET> header:
| Endpoint | Purpose |
|---|---|
GET /api/relayer/pending | Fetch pending payments awaiting processing. |
POST /api/relayer/claim | Atomically claim a payment ({ paymentIntentId }). |
POST /api/relayer/complete | Report success ({ paymentIntentId, txHash }). |
Configuration
# Required
RELAYER_PRIVATE_KEY="" # relayer wallet (needs MINTER_ROLE on DPC, holds USDC)
RELAYER_SECRET="" # shared secret, must match the frontend
FRONTEND_URL="http://localhost:3000"
# Chain & RPC
CHAIN_ID="11155111" # 11155111 = Sepolia, 1 = mainnet
RPC_URLS="https://rpc.sepolia.org" # comma-separated, with fallback
# Contracts (Sepolia defaults)
DPC_TOKEN_ADDRESS="0x6d663c7c401a18208748dF6F909ffc0C68FA1B73"
USDC_ADDRESS="0xC6a054681bEBb1096BB03E3Fc433bD99BAe987d0"
EXCHANGE_ADDRESS="0x0Cc50006445C7E0e08Fb46Bf5d8b1bECf5275Ba1"
# Tuning
POLL_INTERVAL_MS="10000"
MAX_FEE_GWEI="100"
MAX_RETRIES="5"
RETRY_DELAY_MS="5000"
# Gas monitor
GAS_WARN_BALANCE_ETH="0.1"
GAS_POLL_INTERVAL_MS="180000"
# Notifications (optional)
SMTP_HOST="" SMTP_PORT="587" SMTP_USER="" SMTP_PASS="" SMTP_FROM="" SMTP_TO=""
TELEGRAM_BOT_TOKEN="" TELEGRAM_CHAT_ID=""A .env.example template is provided.
Install, run, deploy
npm install # Install dependencies
npm run build # Compile to dist/
npm run dev # Build + run runner.js
npm run deploy # Build + start via PM2 (production)PM2 (ecosystem.config.js) runs two processes: DataPrism Payment Relayer
(runner.js) and DataPrism Gas Monitor (gas.js), each with their own logs under logs/.
Notes
- The relayer wallet must have
MINTER_ROLEon the DPC token and hold USDC (and ETH for gas). - Multiple RPCs are supported via viem's
fallback()transport. - A failed payment stays unclaimed and can be retried by the same or another instance.
- It grants the Exchange an unlimited USDC approval for efficiency, ensure the Exchange is trusted.

