openapi: 3.1.0
info:
  title: BlockVectra JSON-RPC API
  version: 0.1.0
  description: "BlockVectra JSON-RPC 2.0 API. Send requests to
    `https://dev-api.blockvectra.network/v1/robinhood_mainnet` with your API key
    in the `x-api-key` header, or put the key in the path:
    `/v1/{chain}/{api_key}`. All methods are metered in Computation Units (CU)
    and rate-limited per key."
servers:
  - url: https://dev-api.blockvectra.network/v1
    description: BlockVectra JSON-RPC and Data API
tags:
  - name: JSON-RPC
    description: 以太坊 JSON-RPC 2.0 调用。
  - name: Status
    description: 公开服务状态（第 14 节）。
  - name: Chains
    description: 公开链列表与静态参数（第 12 节）。
paths:
  /{chain}/{apiKey}:
    post:
      tags:
        - JSON-RPC
      operationId: rpcWithPathKey
      summary: JSON-RPC call (API key in the URL path)
      description: >
        `POST /v1/{chain}/{api_key}`。`{chain}` 是已服务链的名字；未知链名返回 404 及固定错误对象

        （`unknown_chain`，带 CORS 头），且在读取请求体、查询 key 之前就返回。只使用路径中的 key，忽略

        `x-api-key`/`Authorization` 请求头。`POST /v1`（无斜杠）、`POST
        /v1/{chain}/`（结尾多斜杠、没有 key）等落到 fallback：404 空 body，

        带 CORS 头，不计费。

        本操作下的所有状态码与示例同样适用于 `POST /v1/{chain}`（请求头传 key），差别只在 key 的位置。
      security: []
      parameters:
        - $ref: "#/components/parameters/ChainPath"
        - $ref: "#/components/parameters/ApiKeyPath"
      requestBody:
        $ref: "#/components/requestBodies/JsonRpcBody"
      responses:
        "200":
          $ref: "#/components/responses/Ok"
        "204":
          $ref: "#/components/responses/NoContent"
        "400":
          $ref: "#/components/responses/BadRequest"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFoundPathKey"
        "408":
          $ref: "#/components/responses/RequestTimeout"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /{chain}:
    post:
      tags:
        - JSON-RPC
      operationId: rpcWithHeaderKey
      summary: JSON-RPC call (API key in a header)
      description: >
        `POST /v1/{chain}`，key 放在 `x-api-key` 或 `Authorization: Bearer` 请求头中，非空的
        `x-api-key` 优先。

        `{chain}` 是已服务链的名字；未知链名返回 404 及固定错误对象（`unknown_chain`，带 CORS 头），且在读取

        请求体、查询 key 之前就返回。除鉴权方式外，行为与 `POST /v1/{chain}/{apiKey}` 完全相同，那里的全部状态码与示例

        （400、402、403、408、413、429、503 等）同样适用。
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ChainPath"
      requestBody:
        $ref: "#/components/requestBodies/JsonRpcBody"
      responses:
        "200":
          description: JSON-RPC 响应。与 `POST /v1/{chain}/{apiKey}` 的 200 相同，这里只列出与请求头鉴权相关的示例。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JsonRpcResponseBody"
              examples:
                okXApiKey:
                  $ref: "#/components/examples/okXApiKey"
                okBearer:
                  $ref: "#/components/examples/okBearer"
                okEmptyXApiKeyFallsBackToBearer:
                  $ref: "#/components/examples/okEmptyXApiKeyFallsBackToBearer"
        "204":
          $ref: "#/components/responses/NoContent"
        "400":
          $ref: "#/components/responses/BadRequest"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFoundHeaderKey"
        "408":
          $ref: "#/components/responses/RequestTimeout"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /status:
    get:
      tags:
        - Status
      operationId: getStatus
      summary: Public service status
      description: "`GET /v1/status` returns the current status of the service for the
        public status page. No authentication, no billing, no rate limiting.
        Data is refreshed at most every 30 seconds."
      security: []
      responses:
        "200":
          $ref: "#/components/responses/StatusOk"
  /chains:
    get:
      tags:
        - Chains
      operationId: getChains
      summary: Public chain list and parameters
      description: |
        `GET /v1/chains`。公开链列表与静态参数接口（第 12 节）。免鉴权、不计费、不限流。
        `chains[]` 只列出对外服务的链，按链名排序；包含每条链的 EVM chain_id、支持能力（jsonrpc / data）、
        方法策略（methods allow / deny）、最大日志跨度、状态窗口区块数与公网信息。
        Data is refreshed at most every 60 seconds.
      security: []
      responses:
        "200":
          $ref: "#/components/responses/ChainsOk"
components:
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: x-api-key
      description: API key。非空时优先于 `Authorization`，首尾空白会被去掉。
    BearerAuth:
      type: http
      scheme: bearer
      description: "`Authorization: Bearer <api_key>`，前缀只接受 `Bearer ` 或 `bearer `。仅在
        `x-api-key` 缺失或为空时使用。"
  parameters:
    ChainPath:
      name: chain
      in: path
      required: true
      description: Chain name, the same as in the Data API, e.g. `robinhood_mainnet`.
        The same API key works on every supported chain.
      schema:
        type: string
        pattern: ^[a-z][a-z0-9]*(_[a-z0-9]+)+$
      example: robinhood_mainnet
    ApiKeyPath:
      name: apiKey
      in: path
      required: true
      description: Your API key (`rgw_` followed by 64 hex characters). An unknown or
        disabled key returns 404 with an empty body.
      schema:
        type: string
        minLength: 1
      example: rgw_4f2d0c3b9a8e7d6c5b4a39281706f5e4d3c2b1a09f8e7d6c5b4a3928170605f4
  requestBodies:
    JsonRpcBody:
      required: true
      description: 单个 JSON-RPC 调用对象，或 1～100 个调用组成的批量数组。最大 2 MiB。
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/JsonRpcRequestBody"
          examples:
            reqBlockNumber:
              $ref: "#/components/examples/reqBlockNumber"
            reqChainId:
              $ref: "#/components/examples/reqChainId"
            reqBatch:
              $ref: "#/components/examples/reqBatch"
            reqEthCall:
              $ref: "#/components/examples/reqEthCall"
            reqMixedBatch:
              $ref: "#/components/examples/reqMixedBatch"
            reqNotifications:
              $ref: "#/components/examples/reqNotifications"
            reqSubscribe:
              $ref: "#/components/examples/reqSubscribe"
            reqAmbiguousMember:
              $ref: "#/components/examples/reqAmbiguousMember"
            reqEmptyBatch:
              $ref: "#/components/examples/reqEmptyBatch"
            reqMissingMethod:
              $ref: "#/components/examples/reqMissingMethod"
            reqBalanceOutsideWindow:
              $ref: "#/components/examples/reqBalanceOutsideWindow"
            reqLogsTooWide:
              $ref: "#/components/examples/reqLogsTooWide"
            reqTraceUnknownTx:
              $ref: "#/components/examples/reqTraceUnknownTx"
            reqTraceTx:
              $ref: "#/components/examples/reqTraceTx"
            reqPrunedBlock:
              $ref: "#/components/examples/reqPrunedBlock"
            reqTxReceipt:
              $ref: "#/components/examples/reqTxReceipt"
            reqBlockNumberBatch3:
              $ref: "#/components/examples/reqBlockNumberBatch3"
            reqEthCallBatch3:
              $ref: "#/components/examples/reqEthCallBatch3"
  responses:
    Ok:
      description: |
        JSON-RPC 响应：单个请求对应单个响应对象，批量请求对应按请求顺序排列的数组（notification 不占位）。
        每个响应元素要么有 `result`，要么有 `error`。所有 JSON-RPC 层错误（包括整请求级的 `-32700`、
        `-32600`）都在 HTTP 200 中返回。
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/JsonRpcResponseBody"
          examples:
            okSingle:
              $ref: "#/components/examples/okSingle"
            okBatch:
              $ref: "#/components/examples/okBatch"
            okChainId:
              $ref: "#/components/examples/okChainId"
            okMixedBatch:
              $ref: "#/components/examples/okMixedBatch"
            errParse:
              $ref: "#/components/examples/errParse"
            errInvalidRequestEmptyBatch:
              $ref: "#/components/examples/errInvalidRequestEmptyBatch"
            errInvalidRequestNoMethod:
              $ref: "#/components/examples/errInvalidRequestNoMethod"
            errBatchTooLarge:
              $ref: "#/components/examples/errBatchTooLarge"
            errAmbiguousMember:
              $ref: "#/components/examples/errAmbiguousMember"
            errMethodNotAvailable:
              $ref: "#/components/examples/errMethodNotAvailable"
            errNodeSyncing:
              $ref: "#/components/examples/errNodeSyncing"
            errOutsideStateWindow:
              $ref: "#/components/examples/errOutsideStateWindow"
            errLogsRangeTooLarge:
              $ref: "#/components/examples/errLogsRangeTooLarge"
            errTraceTxNotFound:
              $ref: "#/components/examples/errTraceTxNotFound"
            errNodeExecutionReverted:
              $ref: "#/components/examples/errNodeExecutionReverted"
            errPrunedHistory:
              $ref: "#/components/examples/errPrunedHistory"
            errNodeBatchLevel:
              $ref: "#/components/examples/errNodeBatchLevel"
            errUpstreamUnavailable:
              $ref: "#/components/examples/errUpstreamUnavailable"
            errUpstreamTooLarge:
              $ref: "#/components/examples/errUpstreamTooLarge"
            errGatewayOverloaded:
              $ref: "#/components/examples/errGatewayOverloaded"
    NoContent:
      description: |
        请求中的调用全部是 notification（没有 `id` 成员），服务不返回任何响应体。这些 notification 仍被
        限流、转发，并在节点正常处理后计费；被服务受理的 notification（如 `eth_chainId`）同样按各自方法的 CU 权重计费。
    BadRequest:
      description: |
        服务产生，空 body：HTTP 报文本身畸形，请求没有被处理、不计费。请求行或请求头无法解析时由服务的 HTTP 层
        直接应答（请求头过大为 431、URI 过长为 414）；请求体的 chunked 编码非法、或请求体两次读之间超过 10 s 时，
        在鉴权与余额准入之后、读请求体时应答，随后断开连接。合法 HTTP 但内容不是合法 JSON-RPC 的请求返回
        200 + `-32700`/`-32600`，不是 400。
    PaymentRequired:
      description: 余额不足（-32020）：账户已无可用额度。请求在转发前被拒绝，不计费，不占限流。请在控制台充值后继续。
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/JsonRpcErrorResponse"
          examples:
            paymentRequiredUnfunded:
              $ref: "#/components/examples/paymentRequiredUnfunded"
            paymentRequiredExhausted:
              $ref: "#/components/examples/paymentRequiredExhausted"
    Forbidden:
      description: >
        服务产生，空 body：`/v1/{chain}`、`/v1/{chain}/{api_key}` 只接受 `POST`（`OPTIONS` 是
        CORS 预检，

        返回 204），其他方法（`GET`、`HEAD`、`PUT` 等）不论链名已知还是未知一律 403，不做鉴权、不计费。

        只有 `POST` 到未知链名才返回 404 `unknown_chain`。
    NotFoundPathKey:
      description: Empty body (for missing, unknown, or disabled API key) or a JSON
        error with reason `unknown_chain` (for an unknown chain); a trailing
        slash returns 404, not billed.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/JsonRpcErrorResponse"
          examples:
            notFoundUnknownChain:
              summary: Unknown chain in the path (-32600 unknown chain)
              value:
                jsonrpc: "2.0"
                id: null
                error:
                  code: -32600
                  message: unknown chain
                  data:
                    reason: unknown_chain
            notFoundLegacyKeyPath:
              summary: 旧格式 POST /v1/<key>（key 被当作链名，同样是 unknown chain，不查 key）
              value:
                jsonrpc: "2.0"
                id: null
                error:
                  code: -32600
                  message: unknown chain
                  data:
                    reason: unknown_chain
    NotFoundHeaderKey:
      description: |
        服务产生，空 body：已知链上没有 key、key 未知或被禁用。非空的 `x-api-key` 优先于 `Authorization`，
        因此 `x-api-key` 错误时即使 Bearer 正确也是 404。未知链名返回的是 `NotFoundPathKey` 的 404。
    PayloadTooLarge:
      description: 请求体超过 2 MiB（2,097,152 字节）时返回 413，body 为空。该请求不会被处理，也不计费。
    RequestTimeout:
      description: 从收到请求头起 35 s 内未能返回完整响应时返回 408，body
        为空。此时请求可能已送达节点，并按正常调用计费。不要无条件重试会改变状态的调用（如 eth_sendRawTransaction）。
    TooManyRequests:
      description: "`rate limit exceeded`: retry after the number of seconds in the
        `Retry-After` header. Each key has a CU rate limit and burst capacity
        (shown per key in the console Keys table). `request cost <N> CU exceeds
        burst capacity <M> CU`: a single request exceeds burst capacity;
        retrying will not succeed, split into smaller requests."
      headers:
        Retry-After:
          description: Seconds until the rate limit bucket has refilled enough to serve
            this request (ceiling, at least 1). Only present on rate limit
            exceeded.
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/JsonRpcErrorResponse"
          examples:
            tooManyRequestsRate:
              $ref: "#/components/examples/tooManyRequestsRate"
            tooManyRequestsExceedsBurst:
              $ref: "#/components/examples/tooManyRequestsExceedsBurst"
    ServiceUnavailable:
      description: Billing data is temporarily unavailable; the gateway temporarily
        rejects requests. This is not a balance issue. Requests are not billed;
        retry after the Retry-After interval.
      headers:
        Retry-After:
          description: Recommended wait time in seconds before retrying.
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/JsonRpcErrorResponse"
    StatusOk:
      description: "Current status of the service and the underlying chain. No
        authentication or billing; the response carries
        `Access-Control-Allow-Origin: *` and `Cache-Control: max-age=30`."
      headers:
        Access-Control-Allow-Origin:
          description: CORS allow-list.
          schema:
            type: string
            example: "*"
        Cache-Control:
          description: How long clients and proxies may cache the response.
          schema:
            type: string
            example: max-age=30
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/StatusResponseBody"
          examples:
            statusOk:
              $ref: "#/components/examples/statusOk"
    ChainsOk:
      description: >
        Configured public chains and per-chain static facts. Unauthenticated,
        unmetered, unbilled,

        served with `Access-Control-Allow-Origin: *` and `Cache-Control:
        max-age=60` headers.
      headers:
        Access-Control-Allow-Origin:
          description: CORS allow-list.
          schema:
            type: string
            example: "*"
        Cache-Control:
          description: How long clients and proxies may cache the response.
          schema:
            type: string
            example: max-age=60
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ChainsResponseBody"
          examples:
            chainsOk:
              $ref: "#/components/examples/chainsOk"
  schemas:
    JsonRpcId:
      description: |
        调用 id。建议使用字符串、数字或 `null`。缺少 `id` 成员表示 notification；`"id": null` 不是
        notification。响应中的 `id` 一律等于请求中的原值。
      type:
        - string
        - number
        - "null"
    JsonRpcRequest:
      type: object
      required:
        - method
      description: |
        单个调用。服务只读取 `jsonrpc`/`id`/`method`/`params` 四个成员，转发时按这四个重建对象，
        其他成员丢弃；这四个名字的大小写或形近变体（如 `METHOD`）会使该调用返回 `-32600`。
      properties:
        jsonrpc:
          description: 建议为 `"2.0"`；服务不校验，转发时一律写成 `"2.0"`。
          type: string
          const: "2.0"
        id:
          $ref: "#/components/schemas/JsonRpcId"
        method:
          type: string
          description: 方法名，按第 5 节的方法策略放行或拒绝。
          examples:
            - eth_blockNumber
            - eth_call
            - eth_getLogs
            - debug_traceTransaction
        params:
          description: 原样转发给节点；缺省时转发的对象也不带 `params`。
          type:
            - array
            - object
    JsonRpcBatchRequest:
      type: array
      minItems: 1
      maxItems: 100
      description: 批量请求，1～100 个调用。任一元素不是对象、或缺少字符串 `method`（且不含歧义成员名）时整个请求 `-32600`。
      items:
        $ref: "#/components/schemas/JsonRpcRequest"
    JsonRpcRequestBody:
      oneOf:
        - $ref: "#/components/schemas/JsonRpcRequest"
        - $ref: "#/components/schemas/JsonRpcBatchRequest"
    JsonRpcErrorObject:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: integer
          description: 错误码，见第 4 节。
        message:
          type: string
        data:
          description: 附加数据。错误表 reason 列所列的服务错误携带 reason（及可选扩展字段如 max），其余服务错误不带
            data；节点返回的附加数据（如 revert data）原样透传。
    JsonRpcSuccessResponse:
      type: object
      required:
        - jsonrpc
        - id
        - result
      properties:
        jsonrpc:
          type: string
          const: "2.0"
        id:
          $ref: "#/components/schemas/JsonRpcId"
        result:
          description: 节点给出的结果，可以是任意 JSON 值，包括 `null`。
    JsonRpcErrorResponse:
      type: object
      required:
        - jsonrpc
        - id
        - error
      properties:
        jsonrpc:
          type: string
          const: "2.0"
        id:
          $ref: "#/components/schemas/JsonRpcId"
        error:
          $ref: "#/components/schemas/JsonRpcErrorObject"
    JsonRpcResponse:
      oneOf:
        - $ref: "#/components/schemas/JsonRpcSuccessResponse"
        - $ref: "#/components/schemas/JsonRpcErrorResponse"
    JsonRpcBatchResponse:
      type: array
      description: 按请求顺序排列，每个带 `id` 的调用一个元素，notification 不占位。
      items:
        $ref: "#/components/schemas/JsonRpcResponse"
    JsonRpcResponseBody:
      oneOf:
        - $ref: "#/components/schemas/JsonRpcResponse"
        - $ref: "#/components/schemas/JsonRpcBatchResponse"
    DataChainsResponse:
      type: object
      description: "`GET /v1/data/chains` 成功响应（只含当前对外提供 Data API 的链，与 `GET /v1/status`
        中 `data: true` 的链相同）。"
      required:
        - data
      properties:
        data:
          type: array
          description: 支持的链列表。
          items:
            $ref: "#/components/schemas/DataChain"
        meta:
          type: object
          description: 元数据对象（如 `as_of_block`，原样透传）。
    DataChain:
      type: object
      description: Data API 支持的链配置与运行参数。
      required:
        - chain
        - chain_slug
        - name
        - chain_id
        - chain_external_id
        - features
        - coverage
        - finality
        - limits
      properties:
        chain:
          type: string
          description: 链标识符（slug），用于 `{chain}` 路径段，精确小写匹配。
          example: robinhood_mainnet
        chain_slug:
          type: string
          description: 规范的大写链标识符。
          example: ROBINHOOD_MAINNET
        name:
          type: string
          description: 人类可读显示名。
          example: Robinhood Chain
        chain_id:
          type: integer
          format: int64
          description: EIP-155 EVM 链 ID。
          example: 4663
        chain_external_id:
          type: string
          description: CAIP-2 格式外部链标识符（`eip155:{chain_id}`）。
          example: eip155:4663
        features:
          type: array
          description: 该链支持的能力列表。不在列表中的端点返回 422 `no_coverage`。
          items:
            type: string
          example:
            - blocks
            - transactions
            - address_transactions
            - transfers
            - token_metadata
            - freshness
        coverage:
          $ref: "#/components/schemas/DataChainCoverage"
        finality:
          $ref: "#/components/schemas/DataChainFinality"
        limits:
          $ref: "#/components/schemas/DataChainLimits"
    DataChainCoverage:
      type: object
      description: 该链的数据覆盖范围。
      required:
        - history_mode
        - from_block
        - traces_from_block
      properties:
        history_mode:
          type: string
          enum:
            - full
            - window
          description: "`full` 表示包含从创世区块开始的全量历史；`window` 表示滚动窗口（`retention_days`），仅保留最近数据。"
        from_block:
          type:
            - integer
            - "null"
          format: int64
          description: 覆盖历史的起始区块高度。该字段始终存在。全量链（`full`）始终为整数（通常为
            0）；滚动窗口链（`window`）为当前覆盖下限（随保留分区向前推移，永不后退）。 当底层数据服务无法确定覆盖起点时，该字段为
            `null`。 `null` 仅发生在滚动窗口链上，表示起点未知（不代表 0，也不代表全量历史）；在为 `null`
            期间，客户端不得发送历史数据查询（返回 503 `unavailable`）。
          example: 26000000
        retention_days:
          type: integer
          description: 滚动窗口保留天数下限（仅在 `window` 链上出现）。实际覆盖跨度从不低于 `retention_days`。
        traces_from_block:
          type:
            - integer
            - "null"
          format: int64
          description: Trace 数据的覆盖起始区块高度。若该链不支持 trace、尚无 trace 数据或无法确定，则为 `null`。
          example: 72050949
    DataChainFinality:
      type: object
      description: 最终性判定规则。
      required:
        - model
        - lag_blocks
      properties:
        model:
          type: string
          enum:
            - block_lag
          description: 最终性计算模型。
        lag_blocks:
          type: integer
          format: int64
          description: 相对于链头（`as_of_block`）的落后区块数。
          example: 64
    DataChainLimits:
      type: object
      description: 单请求上限与并发限制（原样透传）。
      required:
        - max_page_size
        - max_window_blocks
        - max_pools_for_token
        - max_batch_addresses
        - max_date_span_days
        - max_concurrent_queries
      properties:
        max_page_size:
          type: integer
          description: 单页最大记录数（`limit` 参数上限）。
          example: 500
        max_window_blocks:
          type: integer
          format: int64
          description: 窗口列表端点单次允许的最大区块跨度（`[from_block, to_block]`）。
          example: 100000
        max_pools_for_token:
          type: integer
          description: 单个代币查询允许跨越的最大交易池数量。
          example: 200
        max_batch_addresses:
          type: integer
          description: 批量代币请求（`POST /{chain}/tokens:batch`）允许传入的最大地址数。
          example: 100
        max_date_span_days:
          type: integer
          description: 价格历史查询允许的最大跨度天数。
          example: 90
        max_concurrent_queries:
          type: integer
          minimum: 2
          description: 底层数据服务对该链允许同时执行的查询并发上限。原样透传该字段。
          example: 2
    ChainsResponse:
      $ref: "#/components/schemas/DataChainsResponse"
    StatusResponseBody:
      type: object
      required:
        - checked_at
        - gateway
        - chains
      properties:
        checked_at:
          type: string
          format: date-time
          description: 状态快照生成时间（RFC 3339 / ISO 8601 UTC）。
        gateway:
          type: object
          required:
            - status
          properties:
            status:
              type: string
              enum:
                - ok
                - degraded
              description: 服务运行状态：`ok` 为进程当前就绪；`degraded` 为余额准入或 key
                数据尚未就绪或已过期，付费请求会被拒绝，恢复后自动回到 `ok`。与任何一条链的节点状态无关，链的状态见
                `chains[].status`。
        chains:
          type: array
          items:
            $ref: "#/components/schemas/ChainStatus"
          description: 支持的区块链及其节点同步状态。
    ChainStatus:
      type: object
      required:
        - chain
        - name
        - chain_id
        - jsonrpc
        - data
        - data_features
        - status
        - sync
        - head
      properties:
        chain:
          type: string
          description: 链标识符（slug）。
        name:
          type: string
          description: 人类可读显示名。
        chain_id:
          type: integer
          description: 链 ID（十进制整数）。
        jsonrpc:
          type: boolean
          description: 是否对外提供 JSON-RPC。
        data:
          type: boolean
          description: 是否对外提供 Data API。不是固定配置：系统最多每 30 秒读取一次 Data API 上游服务的链清单，
            一条已注册的链出现在清单里时为 `true`，被清单去掉后回到 `false`；服务启动后第一次读取成功之前，所有链都是
            `false`。
        data_features:
          type: array
          items:
            type: string
          description: Data API 为该链提供的能力，与 `GET /v1/data/chains` 中该链的 `features`
            的取值和顺序相同；`data` 为 `false` 时为空数组。 不在列表中的端点返回 422 `no_coverage`。
          example:
            - blocks
            - transactions
            - address_transactions
            - transfers
            - token_metadata
            - freshness
        data_status:
          type: string
          enum:
            - ok
            - unavailable
          description: Data API 运行状态。仅在 `data` 为 `true` 时出现；上游数据服务汇报已完成基础索引、有数据可查时为 `ok`，
            正在同步或尚未完成基础索引时为 `unavailable`。`data` 为 `false` 时省略该字段。
        status:
          type: string
          enum:
            - ok
            - unavailable
          description: 链运行状态。
        sync:
          $ref: "#/components/schemas/ChainSync"
        head:
          $ref: "#/components/schemas/ChainHead"
    ChainSync:
      type: object
      description: 节点同步阶段与进度区块。
      required:
        - stage
        - node_block
        - target_block
      properties:
        stage:
          type: string
          enum:
            - synced
            - syncing
            - unknown
          description: 节点同步阶段：`synced` 为节点完全满足健康判定（与流量准入一致，包含区块时间新鲜度）； `syncing`
            为最近一次成功探测获得 `eth_syncing` 进度对象且健康判定未通过； `unknown`
            为其它所有情况（尚未完成首次探测、最近一次探测失败、chain ID 不匹配、`eth_syncing` 无法解析、或
            `eth_syncing` 为 false 但健康判定未通过）。
        node_block:
          type:
            - integer
            - "null"
          nullable: true
          description: 节点当前区块高度（`eth_syncing` 进度对象的 currentBlock，无进度对象时为最新区块高度）；未完成首次探测时为
            null。
        target_block:
          type:
            - integer
            - "null"
          nullable: true
          description: 目标区块高度：`highestBlock` 大于 `currentBlock` 时为 `highestBlock`；否则当进度对象包含
            `stages` 数组时，为大于 `currentBlock` 的最大 `stages[].block`；其它情况为 null。
    ChainHead:
      type:
        - object
        - "null"
      nullable: true
      description: 节点最新区块信息。未完成首次成功探测或节点状态未知（如区块高度为 0 或时间戳为 0）时为 null。
      required:
        - block
        - time
        - lag_seconds
      properties:
        block:
          type: integer
          description: 节点最新区块高度。
        time:
          type: string
          format: date-time
          description: 节点最新区块时间戳（RFC 3339 / ISO 8601 UTC）。
        lag_seconds:
          type: integer
          description: 区块时间与当前时间的落后秒数。
    ChainsResponseBody:
      type: object
      required:
        - chains
      properties:
        chains:
          type: array
          items:
            $ref: "#/components/schemas/ChainFacts"
          description: 公开链列表及其静态参数。
    ChainFacts:
      type: object
      required:
        - chain
        - name
        - chain_id
        - jsonrpc
        - data
        - methods
        - max_logs_block_range
        - state_window_blocks
        - info
      properties:
        chain:
          type: string
          description: 链标识符（slug）。
          example: robinhood_mainnet
        name:
          type: string
          description: 人类可读显示名。
          example: Robinhood
        chain_id:
          type: integer
          description: EIP-155 链 ID（十进制整数）。
          example: 4663
        jsonrpc:
          type: boolean
          description: 是否对外提供 JSON-RPC。
          example: true
        data:
          type: boolean
          description: 是否对外提供 Data API。
          example: true
        methods:
          type: object
          required:
            - allow
            - deny
          properties:
            allow:
              type: array
              items:
                type: string
              description: 允许的方法或前缀通配符列表（非空）。
              example:
                - eth_*
                - net_*
                - web3_*
                - debug_trace*
            deny:
              type: array
              items:
                type: string
              description: 拒绝的方法或前缀通配符列表。
              example:
                - eth_newFilter
        max_logs_block_range:
          type: integer
          description: 单次 `eth_getLogs` 允许的最大区块跨度。
          example: 1000
        state_window_blocks:
          type:
            - integer
            - "null"
          nullable: true
          description: 历史状态窗口大小（区块数）。全历史（归档）或无限制时为 null。
          example: 900
        info:
          type: object
          description: 链公网扩展信息（预留，当前为空对象 `{}`）。
  examples:
    reqBlockNumber:
      summary: 单个调用 eth_blockNumber
      value:
        jsonrpc: "2.0"
        id: 1
        method: eth_blockNumber
        params: []
    reqChainId:
      summary: 单个调用 eth_chainId
      value:
        jsonrpc: "2.0"
        id: 1
        method: eth_chainId
        params: []
    reqBatch:
      summary: 批量 3 个调用
      value:
        - jsonrpc: "2.0"
          id: 1
          method: eth_blockNumber
          params: []
        - jsonrpc: "2.0"
          id: 2
          method: eth_chainId
          params: []
        - jsonrpc: "2.0"
          id: 3
          method: eth_getBalance
          params:
            - "0x1111111111111111111111111111111111111111"
            - latest
    reqEthCall:
      summary: eth_call（latest）
      value:
        jsonrpc: "2.0"
        id: 1
        method: eth_call
        params:
          - to: "0x2222222222222222222222222222222222222222"
            data: "0x06fdde03"
          - latest
    reqMixedBatch:
      summary: 批量：正常调用、被拒绝的方法、跨度过大的 eth_getLogs、一个 notification
      value:
        - jsonrpc: "2.0"
          id: 1
          method: eth_blockNumber
          params: []
        - jsonrpc: "2.0"
          id: 2
          method: eth_subscribe
          params:
            - newHeads
        - jsonrpc: "2.0"
          id: 3
          method: eth_getLogs
          params:
            - fromBlock: "0x45a2409"
              toBlock: "0x45a27f1"
        - jsonrpc: "2.0"
          method: eth_chainId
          params: []
    reqNotifications:
      summary: 只有 notification 的批量
      value:
        - jsonrpc: "2.0"
          method: eth_blockNumber
          params: []
        - jsonrpc: "2.0"
          method: eth_chainId
          params: []
    reqSubscribe:
      summary: eth_subscribe（不支持 WebSocket 订阅）
      value:
        jsonrpc: "2.0"
        id: 1
        method: eth_subscribe
        params:
          - newHeads
    reqAmbiguousMember:
      summary: 同时带 method 和 METHOD 成员
      value:
        jsonrpc: "2.0"
        id: 1
        method: eth_blockNumber
        METHOD: debug_traceCall
        params: []
    reqEmptyBatch:
      summary: 空数组（故意不符合 schema）
      value: []
    reqMissingMethod:
      summary: 缺少 method 成员（故意不符合 schema）
      value:
        jsonrpc: "2.0"
        id: 1
        params: []
    reqBalanceOutsideWindow:
      summary: 链头 − 901 处的 eth_getBalance
      value:
        jsonrpc: "2.0"
        id: 1
        method: eth_getBalance
        params:
          - "0x1111111111111111111111111111111111111111"
          - "0x45a246c"
    reqLogsTooWide:
      summary: 跨 1001 个区块的 eth_getLogs
      value:
        jsonrpc: "2.0"
        id: 1
        method: eth_getLogs
        params:
          - fromBlock: "0x45a2409"
            toBlock: "0x45a27f1"
    reqTraceUnknownTx:
      summary: 节点查不到的交易哈希上的 debug_traceTransaction
      value:
        jsonrpc: "2.0"
        id: 1
        method: debug_traceTransaction
        params:
          - "0xabababababababababababababababababababababababababababababababab"
    reqTraceTx:
      summary: debug_traceTransaction
      value:
        jsonrpc: "2.0"
        id: 1
        method: debug_traceTransaction
        params:
          - "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
    reqPrunedBlock:
      summary: 约 14 天前的区块
      value:
        jsonrpc: "2.0"
        id: 1
        method: eth_getBlockByNumber
        params:
          - "0x40ddcb1"
          - false
    reqTxReceipt:
      summary: eth_getTransactionReceipt
      value:
        jsonrpc: "2.0"
        id: 1
        method: eth_getTransactionReceipt
        params:
          - "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
    reqBlockNumberBatch3:
      summary: 批量 3 个 eth_blockNumber
      value:
        - jsonrpc: "2.0"
          id: 1
          method: eth_blockNumber
          params: []
        - jsonrpc: "2.0"
          id: 2
          method: eth_blockNumber
          params: []
        - jsonrpc: "2.0"
          id: 3
          method: eth_blockNumber
          params: []
    reqEthCallBatch3:
      summary: 批量 3 个 eth_call（3 × eth_call 的权重）
      value:
        - jsonrpc: "2.0"
          id: 1
          method: eth_call
          params:
            - to: "0x2222222222222222222222222222222222222222"
              data: "0x06fdde03"
            - latest
        - jsonrpc: "2.0"
          id: 2
          method: eth_call
          params:
            - to: "0x2222222222222222222222222222222222222222"
              data: "0x06fdde03"
            - latest
        - jsonrpc: "2.0"
          id: 3
          method: eth_call
          params:
            - to: "0x2222222222222222222222222222222222222222"
              data: "0x06fdde03"
            - latest
    okSingle:
      summary: 单个调用成功（reqBlockNumber，按 eth_blockNumber 的权重计费）
      value:
        jsonrpc: "2.0"
        id: 1
        result: "0x45a27f1"
    okBatch:
      summary: 批量全部成功（reqBatch：eth_blockNumber、eth_chainId、eth_getBalance 各按其权重）
      value:
        - jsonrpc: "2.0"
          id: 1
          result: "0x45a27f1"
        - jsonrpc: "2.0"
          id: 2
          result: "0x1237"
        - jsonrpc: "2.0"
          id: 3
          result: "0xde0b6b3a7640000"
    okChainId:
      summary: eth_chainId 的应答（reqChainId，按 eth_chainId 的权重）
      value:
        jsonrpc: "2.0"
        id: 1
        result: "0x1237"
    okMixedBatch:
      summary: 批量中的逐调用错误（reqMixedBatch：eth_blockNumber 与 notification eth_chainId，各按其权重）
      value:
        - jsonrpc: "2.0"
          id: 1
          result: "0x45a27f1"
        - jsonrpc: "2.0"
          id: 2
          error:
            code: -32601
            message: "method not available: eth_subscribe"
        - jsonrpc: "2.0"
          id: 3
          error:
            code: -32602
            message: "eth_getLogs block range too large: max 1000 blocks"
    errParse:
      summary: 请求体不是合法 JSON（原文 `{"jsonrpc":"2.0","id":1,"method":`）
      value:
        jsonrpc: "2.0"
        id: null
        error:
          code: -32700
          message: parse error
    errInvalidRequestEmptyBatch:
      summary: 空批量（reqEmptyBatch）
      value:
        jsonrpc: "2.0"
        id: null
        error:
          code: -32600
          message: invalid request
          data:
            reason: invalid_request
    errInvalidRequestNoMethod:
      summary: 缺少 method（reqMissingMethod）
      value:
        jsonrpc: "2.0"
        id: null
        error:
          code: -32600
          message: invalid request
          data:
            reason: invalid_request
    errBatchTooLarge:
      summary: 批量超过 100 个调用（101 个 eth_blockNumber）
      value:
        jsonrpc: "2.0"
        id: null
        error:
          code: -32600
          message: "batch too large: max 100 calls"
          data:
            reason: batch_too_large
            max: 100
    errAmbiguousMember:
      summary: 成员名歧义（reqAmbiguousMember），逐调用拒绝
      value:
        jsonrpc: "2.0"
        id: 1
        error:
          code: -32600
          message: "invalid request: ambiguous member name"
          data:
            reason: invalid_request
    errMethodNotAvailable:
      summary: 方法策略拒绝（reqSubscribe）
      value:
        jsonrpc: "2.0"
        id: 1
        error:
          code: -32601
          message: "method not available: eth_subscribe"
    errNodeSyncing:
      summary: 节点未同步时的调用（reqBlockNumber）
      value:
        jsonrpc: "2.0"
        id: 1
        error:
          code: -32010
          message: node is syncing; calls are temporarily unavailable
    errOutsideStateWindow:
      summary: 状态类调用超出历史状态窗口（reqBalanceOutsideWindow）
      value:
        jsonrpc: "2.0"
        id: 1
        error:
          code: -32011
          message: historical state is not available beyond the most recent 900 blocks
    errLogsRangeTooLarge:
      summary: eth_getLogs 区块跨度超限（reqLogsTooWide）
      value:
        jsonrpc: "2.0"
        id: 1
        error:
          code: -32602
          message: "eth_getLogs block range too large: max 1000 blocks"
    errTraceTxNotFound:
      summary: trace 哈希预解析查不到交易（reqTraceUnknownTx）
      value:
        jsonrpc: "2.0"
        id: 1
        error:
          code: -32000
          message: transaction not found
          data:
            reason: not_found
    errNodeExecutionReverted:
      summary: 节点返回的执行错误原样透传并计费（reqEthCall，按 eth_call 的权重）
      value:
        jsonrpc: "2.0"
        id: 1
        error:
          code: -32000
          message: execution reverted
    errPrunedHistory:
      summary: 节点已裁剪的历史区块，4444 透传、不计费（reqPrunedBlock）
      value:
        jsonrpc: "2.0"
        id: 1
        error:
          code: 4444
          message: pruned history unavailable
    errNodeBatchLevel:
      summary: 节点提前终止批处理（-32003），只计已应答的调用（reqBlockNumberBatch3，一个 eth_blockNumber）
      value:
        - jsonrpc: "2.0"
          id: 1
          result: "0x45a27f1"
        - jsonrpc: "2.0"
          id: 2
          error:
            code: -32003
            message: response too large
        - jsonrpc: "2.0"
          id: 3
          error:
            code: -32003
            message: response too large
    errUpstreamUnavailable:
      summary: 上游失败（HTTP 5xx、连接失败、超时）脱敏后的错误，不计费（reqTxReceipt）
      value:
        jsonrpc: "2.0"
        id: 1
        error:
          code: -32603
          message: upstream unavailable
          data:
            reason: upstream_unavailable
    errUpstreamTooLarge:
      summary: 上游响应超过大小上限，不计费（reqEthCall）
      value:
        jsonrpc: "2.0"
        id: 1
        error:
          code: -32000
          message: upstream response too large
          data:
            reason: response_too_large
    errGatewayOverloaded:
      summary: 上游并发已满，逐调用拒绝，不计费（reqEthCall）
      value:
        jsonrpc: "2.0"
        id: 1
        error:
          code: -32005
          message: gateway overloaded, retry later
          data:
            reason: overloaded
    okXApiKey:
      summary: key 放在 x-api-key 请求头（reqBlockNumber）
      value:
        jsonrpc: "2.0"
        id: 1
        result: "0x45a27f1"
    okBearer:
      summary: key 放在 Authorization Bearer 请求头（reqBlockNumber）
      value:
        jsonrpc: "2.0"
        id: 1
        result: "0x45a27f1"
    okEmptyXApiKeyFallsBackToBearer:
      summary: x-api-key 为空时使用 Bearer（reqBlockNumber）
      value:
        jsonrpc: "2.0"
        id: 1
        result: "0x45a27f1"
    paymentRequiredUnfunded:
      summary: 账户余额为 0（reqBlockNumber）
      value:
        jsonrpc: "2.0"
        id: null
        error:
          code: -32020
          message: insufficient balance
          data:
            reason: balance_exhausted
    paymentRequiredExhausted:
      summary: 额度耗尽（余额 1 的账户先用掉一个 reqBlockNumber，再发 reqBlockNumber）
      value:
        jsonrpc: "2.0"
        id: null
        error:
          code: -32020
          message: insufficient balance
          data:
            reason: balance_exhausted
    tooManyRequestsRate:
      summary: "Rate limit exceeded: not enough CU left in your key's bucket; retry
        after the Retry-After seconds."
      value:
        jsonrpc: "2.0"
        id: null
        error:
          code: -32005
          message: rate limit exceeded
          data:
            reason: key_rate_limit
    tooManyRequestsExceedsBurst:
      summary: 单个请求（3 个 eth_call）的 CU 超过 burst 40（reqEthCallBatch3）
      value:
        jsonrpc: "2.0"
        id: null
        error:
          code: -32022
          message: request cost <N> CU exceeds burst capacity 40 CU
          data:
            reason: request_exceeds_burst
    statusOk:
      summary: 公开服务状态响应
      value:
        checked_at: 2026-09-28T12:00:00Z
        gateway:
          status: ok
        chains:
          - chain: robinhood_mainnet
            name: Robinhood Chain
            chain_id: 4663
            jsonrpc: true
            data: true
            data_features:
              - blocks
              - transactions
              - address_transactions
              - transfers
              - token_metadata
              - freshness
            status: ok
            sync:
              stage: synced
              node_block: 73017329
              target_block: null
            head:
              block: 73017329
              time: 2026-09-28T11:59:58Z
              lag_seconds: 2
    chainsOk:
      summary: 公开链列表与静态参数响应
      value:
        chains:
          - chain: robinhood_mainnet
            name: Robinhood Chain
            chain_id: 4663
            jsonrpc: true
            data: true
            methods:
              allow:
                - eth_*
                - net_*
                - web3_*
                - debug_trace*
              deny:
                - eth_newFilter
                - eth_newBlockFilter
                - eth_newPendingTransactionFilter
                - eth_getFilterLogs
                - eth_getFilterChanges
                - eth_uninstallFilter
                - eth_subscribe
                - eth_unsubscribe
            max_logs_block_range: 1000
            state_window_blocks: 900
            info: {}
