openapi: 3.0.0
paths:
  /metrics:
    get:
      operationId: MetricsController_get
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - Metrics
  /health:
    get:
      operationId: HealthController_health
      summary: Liveness probe — no dependency checks.
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponseDto'
      tags:
        - Health
  /readiness:
    get:
      operationId: HealthController_readiness
      summary: Readiness probe — verifies signer and first-chain RPC reachable.
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReadinessResponseDto'
        '503':
          description: A dependency (signer or chain RPC) is unreachable.
      tags:
        - Health
  /api/whitelist:
    post:
      operationId: AdminController_whitelist
      summary: Add or remove a target contract from the per-chain policy allowlist.
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WhitelistBodyDto'
      responses:
        '200':
          description: Updated. Returns `{ok, action, address, size}`.
        '400':
          description: Body failed zod validation.
        '401':
          description: Missing or invalid Authorization bearer.
      tags:
        - Admin
      security:
        - admin-bearer: []
    get:
      operationId: AdminController_whitelistAll
      summary: Current target-contract allowlist (per-chain map).
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - Admin
      security:
        - admin-bearer: []
  /:
    post:
      operationId: RpcController_root
      summary: >-
        JSON-RPC entry — pm_sponsorUserOperation, pm_getPaymasterData,
        pm_getPaymasterStubData
      description: >-
        ERC-4337 + ERC-7677 sponsor-RPC. The body is a JSON-RPC 2.0 request; the
        response is the matching JSON-RPC 2.0 response (success or error
        envelope).
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/JsonRpcRequestDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JsonRpcResponseDto'
      tags:
        - RPC
  /rpc:
    post:
      operationId: RpcController_rpcPath
      summary: Same as POST / — alternate path some bundlers default to.
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/JsonRpcRequestDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JsonRpcResponseDto'
      tags:
        - RPC
  /bundler/{chainId}:
    post:
      operationId: BundlerController_proxy
      parameters:
        - name: chainId
          required: true
          in: path
          schema:
            type: number
      responses:
        '201':
          description: ''
      tags:
        - Bundler
  /info/chains:
    get:
      operationId: InfoController_chains
      summary: >-
        List supported chains, paymaster contract addresses, ERC-20 sponsorship
        tokens.
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChainInfoListDto'
      tags:
        - Info
  /info/deposit:
    get:
      operationId: InfoController_deposits
      summary: EntryPoint deposit balance across configured chains.
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DepositInfoListDto'
      tags:
        - Info
  /info/{chainId}/deposit:
    get:
      operationId: InfoController_depositForChain
      summary: EntryPoint deposit balance for a single chain.
      parameters:
        - name: chainId
          required: true
          in: path
          schema:
            type: number
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DepositInfoDto'
        '404':
          description: Chain not found
      tags:
        - Info
  /info/{chainId}/estimate:
    get:
      operationId: InfoController_estimate
      summary: Gas + cost estimate for the next sponsored op on a chain.
      description: >-
        Returns the gas component breakdown, current gwei + ETH/USD price, and
        (when `token=` is passed) the equivalent ERC-20 amount.
      parameters:
        - name: chainId
          required: true
          in: path
          schema:
            type: number
        - name: token
          required: false
          in: query
          description: ERC-20 address — switches estimate into ERC-20-fee mode.
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EstimateResponseDto'
      tags:
        - Info
  /info/stats:
    get:
      operationId: InfoController_stats
      summary: Per-process request + sponsorship stats (observability).
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - Info
info:
  title: StablecoinX Paymaster
  description: >-
    ERC-4337 paymaster RPC service. Supports EntryPoint v0.8 (canonical, always
    configured) and v0.7 (optional peer for clients that can't speak v0.8 yet).
    JSON-RPC `pm_sponsorUserOperation`, `pm_getPaymasterData`, and
    `pm_getPaymasterStubData` (ERC-7677) for sponsored and ERC-20-paid gas
    modes, plus read-only `/info/*` endpoints (chains, deposit, estimate) and
    admin `/api/*` endpoints (whitelist mutation, guarded by `Authorization:
    Bearer <ADMIN_API_KEY>`).
  version: '1.0'
  contact: {}
tags:
  - name: RPC
    description: ERC-4337 JSON-RPC — pm_sponsorUserOperation, pm_getPaymasterData
  - name: Info
    description: Read-only — supported chains, paymaster deposits, gas estimates
  - name: Bundler
    description: Per-chain bundler proxy (Pimlico API key stays server-side)
  - name: Admin
    description: Whitelist mutation — requires x-admin-api-key header
  - name: Health
    description: Liveness + readiness
  - name: Metrics
    description: Prometheus scrape endpoint
servers:
  - url: https://paymaster.harness.stablecoinx.com
    description: Production
  - url: https://paymaster.dev.scx.easeflow.io
    description: Dev
components:
  securitySchemes:
    admin-bearer:
      scheme: bearer
      bearerFormat: JWT
      type: http
      description: ADMIN_API_KEY for /api/* write endpoints
  schemas:
    HealthResponseDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - ok
      required:
        - status
    ReadinessResponseDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - ready
        signer:
          type: string
          pattern: ^0x[0-9a-fA-F]{40}$
      required:
        - status
        - signer
    WhitelistBodyDto:
      type: object
      properties:
        action:
          type: string
          enum:
            - add
            - remove
        address:
          type: string
          pattern: ^0x[0-9a-fA-F]{40}$
      required:
        - action
        - address
    JsonRpcRequestDto:
      type: object
      properties:
        jsonrpc:
          type: string
          enum:
            - '2.0'
        id:
          oneOf:
            - type: number
            - type: string
        method:
          type: string
        params:
          type: array
          items: {}
      required:
        - jsonrpc
        - id
        - method
        - params
    JsonRpcResponseDto:
      type: object
      properties:
        jsonrpc:
          type: string
          enum:
            - '2.0'
        id:
          oneOf:
            - type: number
            - type: string
            - {}
        result: {}
        error:
          type: object
          properties:
            code:
              type: number
            message:
              type: string
          required:
            - code
            - message
      required:
        - jsonrpc
        - id
    ChainInfoListDto:
      type: array
      items:
        type: object
        properties:
          chainId:
            type: integer
          paymaster:
            type: string
            pattern: ^0x[0-9a-fA-F]{40}$
          entryPoint:
            type: string
            pattern: ^0x[0-9a-fA-F]{40}$
          tokens:
            type: array
            items:
              type: object
              properties:
                name:
                  type: string
                address:
                  type: string
                  pattern: ^0x[0-9a-fA-F]{40}$
                decimals:
                  type: integer
              required:
                - name
                - address
                - decimals
        required:
          - chainId
          - paymaster
          - entryPoint
          - tokens
    DepositInfoListDto:
      type: array
      items:
        type: object
        properties:
          balanceEth:
            type: string
          chainId:
            type: integer
        required:
          - balanceEth
          - chainId
    DepositInfoDto:
      type: object
      properties:
        balanceEth:
          type: string
        chainId:
          type: integer
      required:
        - balanceEth
        - chainId
    EstimateResponseDto:
      type: object
      properties:
        chainId:
          type: integer
        gas:
          type: object
          properties:
            callGasLimit:
              type: string
            verificationGasLimit:
              type: string
            preVerificationGas:
              type: string
            paymasterVerificationGasLimit:
              type: string
            paymasterPostOpGasLimit:
              type: string
            total:
              type: string
          required:
            - callGasLimit
            - verificationGasLimit
            - preVerificationGas
            - paymasterVerificationGasLimit
            - paymasterPostOpGasLimit
            - total
        gasPriceGwei:
          type: number
        ethUsdPrice:
          type: number
        costEth:
          type: string
        costUsd:
          type: number
        token:
          type: object
          properties:
            address:
              type: string
              pattern: ^0x[0-9a-fA-F]{40}$
            symbol:
              type: string
            decimals:
              type: integer
            exchangeRate:
              type: string
            costToken:
              type: string
          required:
            - address
            - symbol
            - decimals
            - exchangeRate
            - costToken
      required:
        - chainId
        - gas
        - gasPriceGwei
        - ethUsdPrice
        - costEth
        - costUsd
