# StablecoinX Paymaster — Full reference > Extended reference for LLM retrieval. See [llms.txt](https://paymaster-ui.harness.stablecoinx.com/llms.txt) for the short summary. The OpenAPI 3.x spec at [https://paymaster.harness.stablecoinx.com/openapi.json](https://paymaster.harness.stablecoinx.com/openapi.json) is the typed contract for the direct paymaster surface. ## Two paymaster surfaces There are two distinct paymaster RPC paths in the StablecoinX monorepo. Choose by integration shape. ### Direct paymaster — `paymaster.harness.stablecoinx.com` NestJS service in `packages/paymaster` (port 8080 in dev). JSON-RPC at `/` and `/rpc`. No clientId concept. Per-sender policy gates. Used by `paymaster-ui` (this test surface) and by `treasury-ui` for sponsored smart-account ops. - **Auth:** none for read RPC + `/info/*`. `/api/*` admin write endpoints require `Authorization: Bearer ` (no key configured ⇒ guard is a no-op, suitable for local dev). - **Per-sender policy** (per smart-account address, rolling 60-minute window): - `POLICY_RATE_LIMIT_PER_HOUR` — max sponsored UserOps. Default 20; `0` disables. - `POLICY_DAILY_SPEND_CAP_USD` — max sponsored USD value per UTC day. Default 50; `0` disables. - `POLICY_TARGET_ALLOWLIST` — comma-separated lowercase target contract addresses. Empty ⇒ all targets allowed. ### Managed paymaster — `api.harness.stablecoinx.com/v1/paymaster/rpc` Lives inside the `api` service (`packages/api/src/paymaster`). Used by `business-ui` and third-party dApps for per-merchant attribution and isolation. - **Auth:** required `X-Client-Id: pmc_live_*` (production) or `pmc_test_*` (testing) header. - **Per-client enforcement** (in `PaymasterClientGuard`): - `allowedOrigins` — when configured, the request's `Origin` header (normalised to scheme + host + port) must be on the list. Missing or non-allowlisted ⇒ 403 `paymaster_origin_not_allowed`. - `allowedChainIds` — `chainId` is extracted from the JSON-RPC params (position 2 for `pm_sponsorUserOperation`, position 3 for `pm_getPaymasterData` / `pm_getPaymasterStubData`); requests outside the allowlist ⇒ 403 `paymaster_chain_not_allowed`. - Missing / unknown / revoked clientId ⇒ 401 `paymaster_unauthorized`. - **Clients** are CRUD-managed at `https://business.harness.stablecoinx.com/paymaster/clients` (merchant-auth gated; backs onto `/v1/paymaster/clients`). ## JSON-RPC methods (both surfaces) All three methods conform to ERC-7677. Errors use the JSON-RPC error envelope: ``` {"jsonrpc":"2.0","id":,"error":{"code":,"message":"..."}} ``` `code` follows the JSON-RPC error code convention; HTTP 429 maps to `RATE_LIMITED`. ### `pm_sponsorUserOperation(userOp, entryPoint, [context])` - `userOp` — the standard ERC-4337 UserOperation packed struct (v0.7 or v0.8 layout based on `entryPoint`). - `entryPoint` — `0x4337…108` for v0.8 (canonical), `0x000000…0032` for v0.7 (optional peer). The paymaster service routes the request to the corresponding SingletonPaymaster contract per `resolve-paymaster.ts`. - `context` (optional) — `{"token": "0x…"}` to switch to ERC-20-paid gas mode (the paymaster pulls the named ERC-20 from the sender for gas settlement). Omit for sponsor mode (paymaster pays gas in ETH). Returns the paymaster signature blob to attach to the UserOperation before submission to the bundler. ### `pm_getPaymasterData(userOp, entryPoint, chainId, context)` (ERC-7677) Same intent as `pm_sponsorUserOperation` but uses the standardised ERC-7677 shape: `chainId` is an explicit param, `context` is the policy/token context. Returns paymaster data. ### `pm_getPaymasterStubData(userOp, entryPoint, chainId, context)` (ERC-7677) Returns dummy paymaster data for gas estimation — used by wallets to estimate the UserOp gas before the real sponsorship call. ## REST — `/info/*` (direct paymaster only) All `/info/*` endpoints are public. Returns standard HTTP status codes; error body `{"error":{"message":"..."}}`. ### `GET /info/chains` Returns the array of supported chains with paymaster contract + EntryPoint + ERC-20 tokens configured for sponsorship. Sample shape: ``` [ { "chainId": 11155111, "paymaster": "0xca54…626", "entryPoint": "0x4337…108", "tokens": [{"name":"usde","address":"0x35A0…","decimals":18}] }, ... ] ``` ### `GET /info/deposit` Returns the EntryPoint deposit balance for the first chain (legacy convenience). ### `GET /info/{chainId}/deposit` Returns `{balanceEth: string, chainId: number}` for the named chain. `404` if `chainId` isn't in the supported set. ### `GET /info/{chainId}/estimate?token={addr}` Returns gas cost estimate for the next sponsored op on `chainId`: ``` { "chainId": 11155111, "gas": { "callGasLimit":"…", "verificationGasLimit":"…", "preVerificationGas":"…", "paymasterVerificationGasLimit":"…", "paymasterPostOpGasLimit":"…", "total":"…" }, "gasPriceGwei": 0.12, "ethUsdPrice": 3450.5, "costEth": "0.000001…", "costUsd": 0.0034, "token": { // present only when ?token= is passed "address":"0x…", "symbol":"USDE", "decimals":18, "exchangeRate":"1.0001", "costToken":"0.0034" } } ``` ### `GET /info/stats` Admin observability — request count, error breakdown, last-N sponsorship summaries. ## Admin — `/api/*` (direct paymaster only, requires `Authorization: Bearer`) ### `POST /api/whitelist` Body: `{"action":"add"|"remove","address":"0x…"}`. Mutates the per-chain target-contract allowlist used by `PolicyService`. ## Bundler proxy — `/bundler/{chainId}` `POST /bundler/{chainId}` with a standard ERC-4337 bundler JSON-RPC body. The paymaster service forwards to the real bundler URL configured per chain in `CHAINS_CONFIG`, so the third-party bundler API key stays server-side. ## Contracts (Sepolia testnet) - EntryPoint v0.8 (canonical): `0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108` - EntryPoint v0.7 (optional peer): `0x0000000071727De22E5E9d8BAf0edAc6f37da032` - SingletonPaymasterV8 (used by paymaster-ui test flows): `0xca54A37cA709F14F2E8194755Fe3011fb4345626` `SingletonPaymasterV7` peers are deployed per-chain alongside V8 when needed for clients that can't speak v0.8 yet (e.g. thirdweb's smart-wallet stack). The paymaster service binds both EntryPoint versions per chain and routes incoming RPC calls to the correct SingletonPaymaster contract based on the `entryPoint` param. ## Health & metrics - `GET /health` → `{"status":"ok"}` (liveness, no dependency checks). - `GET /readiness` → `{"status":"ready","signer":"0x…"}` (verifies signer + first chain RPC reachable). `503` with `{"status":"not ready","error":"…"}` when a dependency is down. - `GET /metrics` → Prometheus text-format scrape endpoint. ## StablecoinX MCP server Programmatic, agent-friendly control of a StablecoinX merchant account. Published to npm as [`@stablecoinx/mcp`](https://www.npmjs.com/package/@stablecoinx/mcp); source at https://github.com/e2xlabs/stablecoinx-mcp; registered in the MCP registry as `io.github.e2xlabs/stablecoinx-mcp`. - **Install (Claude Code and clients with an `mcp add` CLI):** `npx @stablecoinx/mcp setup` — installs the bundled `stablecoinx` skill into `~/.claude/skills` and registers the MCP (user scope) with a freshly generated state passphrase. Restart your MCP client afterwards. - **Manual registration:** ``` claude mcp add stablecoinx-mcp -s user \ -e SCX_STATE_PASSPHRASE="$(openssl rand -hex 16)" \ -- npx -y @stablecoinx/mcp ``` Requires Node.js 20+. `SCX_STATE_PASSPHRASE` (any string) is the only required input — the harness API, thirdweb client, origin and login chain are baked into the package. - **Transport:** local stdio server launched via `npx`; all credentials stay on the local machine, encrypted at rest. There is no hosted/remote MCP endpoint. - **Networks:** the harness resolves chain selection (sandbox testnet vs live mainnet) server-side per the merchant account; nothing chain-related to configure on the client. - **Auth:** thirdweb email-OTP → 24h JWT cached in the encrypted local state file. Run `auth_send_otp` then `auth_verify_otp`; mint an `sk_*` with `api_key_create` for server-to-server session creation. - **Tools (27):** auth & onboarding (`auth_status`, `auth_send_otp`, `auth_verify_otp`, `auth_logout`, `api_key_create`, `session_key_create`); merchant profile (`merchant_get`, `merchant_update`); API keys (`api_keys_list`, `api_keys_revoke`); sessions (`sessions_list`, `sessions_get`, `sessions_create_dashboard`, `sessions_create_s2s`); paymaster clients (`paymaster_clients_list` / `_create` / `_update` / `_revoke`); paymaster allowlist (`paymaster_allowlist_list` / `_add` / `_remove`); paymaster usage (`paymaster_usage_get`); webhooks (`webhooks_create` / `_list` / `_delete` / `_rotate_secret` / `_deliveries`). ## Security - Vulnerability disclosure: `tech@e2xlabs.com` (see [/.well-known/security.txt](https://paymaster-ui.harness.stablecoinx.com/.well-known/security.txt)). - Admin endpoints are bearer-token gated. Bundler proxy never echoes the third-party bundler API key back to clients. - Policy layer (per-sender + target allowlist) makes it impractical to abuse the public sponsor RPC at scale. ## Caveats - Gas estimates from `/info/{chainId}/estimate` are based on current ETH price and gas oracle; actual on-chain cost can differ on volatile networks. - ERC-20 fee mode requires the named token to be in the chain's configured `tokens` list. Unsupported tokens return an RPC error. - `pm_getPaymasterStubData` is for estimation only — using it as a final signature won't validate at the EntryPoint.