asyncapi: 3.0.0

info:
  title: Tripport — Ethereum JSON-RPC Pub/Sub WS
  version: 0.2.0-api-vector-s3
  description: |
    Ethereum JSON-RPC over WebSocket. Subscribe / unsubscribe methods
    follow the standard `eth_subscribe` / `eth_unsubscribe` shape; the
    server pushes `eth_subscription` notifications carrying a subscription
    id + result payload.

    Four subscription types — per-method tier requirements mirror
    `tier-matrix.yaml::tiers/*/features/eth_ws_pubsub/allowed_methods`
    (lowest unlocking tier listed):
      - newPendingTransactions  (free)   — mempool tx-hash firehose
      - logs                    (basic)  — filtered log events
      - newHeads                (pro)    — every new block header
      - syncing                 (pro)    — sync state transitions

    A subscribe below the required tier yields WS close 4003 with the
    canonical `forbidden` error frame (see `_shared.yaml::WSErrorFrame`).

    Error envelope: WSErrorFrame from `_shared.yaml`; canonical close
    codes 4001/4003/4029/4030 per PLAN_TECH §11.

x-tier-vector-version: 1
x-ws-close-codes:
  $ref: '_shared.yaml#/x-ws-close-codes'

defaultContentType: application/json

servers:
  production:
    $ref: '_shared.yaml#/servers/production'
  staging:
    $ref: '_shared.yaml#/servers/staging'

channels:

  /ws/eth:
    address: /ws/eth
    description: |
      Ethereum WebSocket pubsub. Client sends `eth_subscribe` to receive
      a subscription id, then receives `eth_subscription` notifications
      until `eth_unsubscribe` (or disconnect).
    bindings:
      ws:
        bindingVersion: 0.1.0
        query:
          type: object
          properties:
            api-key:
              type: string
              description: Legacy static API key; the `x-token` header is the primary form.
    messages:
      EthSubscribe:             {$ref: '#/components/messages/EthSubscribe'}
      EthUnsubscribe:           {$ref: '#/components/messages/EthUnsubscribe'}
      EthSubscriptionAck:       {$ref: '#/components/messages/EthSubscriptionAck'}
      NewHeadsNotification:     {$ref: '#/components/messages/NewHeadsNotification'}
      LogsNotification:         {$ref: '#/components/messages/LogsNotification'}
      NewPendingTxNotification: {$ref: '#/components/messages/NewPendingTxNotification'}
      SyncingNotification:      {$ref: '#/components/messages/SyncingNotification'}
      WSError:                  {$ref: '_shared.yaml#/components/messages/WSError'}

operations:

  sendSubscribe:
    action: send
    channel: {$ref: '#/channels/~1ws~1eth'}
    summary: Send eth_subscribe (one of 4 subscription types).
    description: |
      Client → server. Server responds with subscription id. Required tier
      depends on the subscription type in `params[0]` — see info.description
      and `EthSubscribe.x-tier-method-required` (matrix-derived).
    x-tier-category: eth_ws_pubsub
    x-tier-method-allowlist-ref: proxy-agg/config/tier-matrix.yaml#/tiers/*/features/eth_ws_pubsub/allowed_methods
    messages:
      - {$ref: '#/channels/~1ws~1eth/messages/EthSubscribe'}

  sendUnsubscribe:
    action: send
    channel: {$ref: '#/channels/~1ws~1eth'}
    summary: Send eth_unsubscribe — releases the subscription id.
    messages:
      - {$ref: '#/channels/~1ws~1eth/messages/EthUnsubscribe'}

  receiveNotification:
    action: receive
    channel: {$ref: '#/channels/~1ws~1eth'}
    summary: Server-pushed eth_subscription notifications.
    description: |
      Server → client. Each frame carries one of the 4 notification kinds
      narrowed by `params.result` shape. WSError precedes a canonical
      close.
    messages:
      - {$ref: '#/channels/~1ws~1eth/messages/EthSubscriptionAck'}
      - {$ref: '#/channels/~1ws~1eth/messages/NewHeadsNotification'}
      - {$ref: '#/channels/~1ws~1eth/messages/LogsNotification'}
      - {$ref: '#/channels/~1ws~1eth/messages/NewPendingTxNotification'}
      - {$ref: '#/channels/~1ws~1eth/messages/SyncingNotification'}
      - {$ref: '#/channels/~1ws~1eth/messages/WSError'}

components:

  schemas:

    EthSubscriptionType:
      type: string
      enum: [newHeads, logs, newPendingTransactions, syncing]

    LogsFilter:
      type: object
      description: Filter passed as 2nd `eth_subscribe('logs', ...)` param.
      properties:
        address:
          oneOf:
            - {type: string, description: "single contract"}
            - {type: array, items: {type: string}, description: "multiple"}
        topics:
          type: array
          description: "Up to 4 indexed-topic slots; null = wildcard."
          items:
            oneOf:
              - {type: string}
              - {type: array, items: {type: string}}
              - {type: 'null'}

    NewPendingTxOpts:
      type: boolean
      default: false
      description: |
        Optional 2nd param to `newPendingTransactions`, a plain boolean.
        `true` makes each notification carry the full pending transaction
        object; omitted or `false` sends the tx hash only. An object such as
        `{"includeFullTx": true}` is not accepted: the subscribe request gets
        no response at all (measured 2026-09-22, three connections).

    EthSubscribeRequest:
      type: object
      required: [jsonrpc, id, method, params]
      properties:
        jsonrpc: {type: string, const: "2.0"}
        id:
          oneOf: [{type: integer}, {type: string}]
        method: {type: string, const: eth_subscribe}
        params:
          type: array
          minItems: 1
          maxItems: 2
          items:
            oneOf:
              - {$ref: '#/components/schemas/EthSubscriptionType'}
              - {$ref: '#/components/schemas/LogsFilter'}
              - {$ref: '#/components/schemas/NewPendingTxOpts'}

    EthUnsubscribeRequest:
      type: object
      required: [jsonrpc, id, method, params]
      properties:
        jsonrpc: {type: string, const: "2.0"}
        id:
          oneOf: [{type: integer}, {type: string}]
        method: {type: string, const: eth_unsubscribe}
        params:
          type: array
          minItems: 1
          maxItems: 1
          items: {type: string, description: "hex subscription id"}

    EthSubscriptionAckPayload:
      type: object
      required: [jsonrpc, id, result]
      properties:
        jsonrpc: {type: string, const: "2.0"}
        id:
          oneOf: [{type: integer}, {type: string}]
        result:
          type: string
          description: Hex-encoded subscription id (use for unsubscribe).

    EthBlockHeader:
      type: object
      description: Block header pushed by `newHeads`.
      properties:
        number:           {type: string, description: "hex block height"}
        hash:             {type: string}
        parentHash:       {type: string}
        nonce:            {type: string}
        sha3Uncles:       {type: string}
        logsBloom:        {type: string}
        transactionsRoot: {type: string}
        stateRoot:        {type: string}
        receiptsRoot:     {type: string}
        miner:            {type: string}
        difficulty:       {type: string}
        extraData:        {type: string}
        gasLimit:         {type: string}
        gasUsed:          {type: string}
        timestamp:        {type: string}
        baseFeePerGas:    {type: string, nullable: true}

    EthLog:
      type: object
      description: Single log event pushed by `logs` subscription.
      properties:
        address:          {type: string}
        topics:
          type: array
          items: {type: string}
        data:             {type: string}
        blockNumber:      {type: string}
        transactionHash:  {type: string}
        transactionIndex: {type: string}
        blockHash:        {type: string}
        logIndex:         {type: string}
        removed:          {type: boolean}

    EthSyncingState:
      oneOf:
        - {type: boolean, description: "false when fully synced"}
        - type: object
          properties:
            syncing:      {type: boolean}
            status:
              type: object
              properties:
                startingBlock: {type: string}
                currentBlock:  {type: string}
                highestBlock:  {type: string}

    NotificationEnvelope:
      type: object
      required: [jsonrpc, method, params]
      properties:
        jsonrpc: {type: string, const: "2.0"}
        method:  {type: string, const: eth_subscription}
        params:
          type: object
          required: [subscription, result]
          properties:
            subscription: {type: string, description: "hex subscription id"}
            result: {}

  messages:

    EthSubscribe:
      title: eth_subscribe
      summary: Client → server subscribe (newHeads/logs/newPendingTransactions/syncing).
      x-tier-category: eth_ws_pubsub
      x-tier-method-required:
        newPendingTransactions: free
        logs: basic
        newHeads: pro
        syncing: pro
      payload:
        $ref: '#/components/schemas/EthSubscribeRequest'

    EthUnsubscribe:
      title: eth_unsubscribe
      summary: Client → server unsubscribe by id.
      payload:
        $ref: '#/components/schemas/EthUnsubscribeRequest'

    EthSubscriptionAck:
      title: eth_subscribe response
      summary: Server → client; returns the hex subscription id.
      payload:
        $ref: '#/components/schemas/EthSubscriptionAckPayload'

    NewHeadsNotification:
      title: newHeads notification
      summary: Server-pushed block header on every new finalized head.
      payload:
        allOf:
          - $ref: '#/components/schemas/NotificationEnvelope'
          - type: object
            properties:
              params:
                type: object
                properties:
                  result: {$ref: '#/components/schemas/EthBlockHeader'}

    LogsNotification:
      title: logs notification
      summary: Server-pushed log entry matching the filter.
      payload:
        allOf:
          - $ref: '#/components/schemas/NotificationEnvelope'
          - type: object
            properties:
              params:
                type: object
                properties:
                  result: {$ref: '#/components/schemas/EthLog'}

    NewPendingTxNotification:
      title: newPendingTransactions notification
      summary: Server-pushed pending tx hash (or the full tx object when subscribed with `true` as the 2nd param).
      payload:
        allOf:
          - $ref: '#/components/schemas/NotificationEnvelope'
          - type: object
            properties:
              params:
                type: object
                properties:
                  result:
                    oneOf:
                      - {type: string, description: "tx hash (default mode)"}
                      - {type: object, description: "full tx object (2nd param `true`)"}

    SyncingNotification:
      title: syncing notification
      summary: Server-pushed sync-state transitions.
      payload:
        allOf:
          - $ref: '#/components/schemas/NotificationEnvelope'
          - type: object
            properties:
              params:
                type: object
                properties:
                  result: {$ref: '#/components/schemas/EthSyncingState'}
