Skip to content

blockfrost/blockfrost-websocket-link

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Made by Five Binaries


blockfrost-websocket-link


WebSocket link for Blockfrost.io API.

About • Installation • Usage • UI Explorer • Documentation

About

WebSocket Link is a server-side application that serves as a WebSocket bridge to the Cardano blockchain using the Blockfrost REST API. blockfrost-websocket-link is designed to be run in your infrastructure. It can connect to the public Blockfrost REST API (using your token) or to your own local blockfrost-backend-ryo instance.

Installation

Dev

yarn dev

Production

yarn start

Nix

The Nix files are automatically regenerated by yarn using the yarn-plugin-nixify. You can build the server by running:

$ nix-build

Usage

In order to start the server, you need to configure some environment variables.

Required

  • BLOCKFROST_NETWORK: lets you choose which network it should connect to (mainnet or testnet)
  • BLOCKFROST_PROJECT_ID: Blockfrost.io's project token

Optional

  • BLOCKFROST_BACKEND_URL: URL pointing to your own backend (blockfrost-backend-ryo) if you prefer not to use the public Blockfrost REST API
  • BLOCKFROST_BLOCK_LISTEN_INTERVAL: how often should be the server pulling the backend for new data (in milliseconds, default 5000)
  • BLOCKFROST_FIAT_RATES_PROXY: the proxy used to fetch fiat rates
  • BLOCKFROST_SENTRY_DSN: the Sentry data source name to optionally monitor the service
  • BLOCKFROST_WSLINK_DEBUG: enables debug logging level
  • BUILD_COMMIT: provided by /status endpoint
  • METRICS_COLLECTOR_INTERVAL_MS: frequency for refreshing metrics and performing health check (default 10000)
  • PORT: which port the server should listen to (default 3005)

Once your server has started, you can connect to it.

$ wscat -c ws://localhost:3005 -n
Connected (press CTRL+C to quit)
> {"id":0,"command":"GET_SERVER_INFO","params":{}}
< {"id":0,"type":"message","data":{"name":"Cardano","shortcut":"ada","testnet":false,"version":"0.14.0","decimals":6,"blockHeight":6576695,"blockHash":"2f82b09b22c54806cd61d83d814151bf11fdcd3fb46af231a5c3ec8c85bbb5e3"}}
> {"id":1,"command":"GET_BLOCK","params":{"hashOrNumber":"5f20df933584822601f9e3f8c024eb5eb252fe8cefb24d1317dc3d432e940ebb"}}
< {"id":1,"type":"message","data":{"time":1506203091,"height":null,"hash":"5f20df933584822601f9e3f8c024eb5eb252fe8cefb24d1317dc3d432e940ebb","slot":null,"epoch":null,"epoch_slot":null,"slot_leader":"Genesis slot leader","size":0,"tx_count":14505,"output":"31112484745000000","fees":"0","block_vrf":null,"previous_block":null,"next_block":"89d9b5a5b8ddc8d7e5a6795e9774d97faf1efea59b2caf7eaf9f8c5b32059df4","confirmations":6576696}}
> {"id":2,"command":"SUBSCRIBE_BLOCK","params":{}}
< {"id":2,"type":"message","data":{"subscribed":true}}
< {"id":2,"type":"message","data":{"time":1638458991,"height":6576700,"hash":"4f4ebd2246a7c9f61624c6eaf09038d36474ba9be788526637a0d68ccb50ad93","slot":46892700,"epoch":306,"epoch_slot":63900,"slot_leader":"pool12g6gfm5c4q7rzwwxdmh0xdxk07tyvujp6d9tke2m46k7q7cz7p0","size":71037,"tx_count":37,"output":"5608976268607","fees":"10040718","block_vrf":"vrf_vk1tpln5vj8nlgrlxac5t0umc546mqdusvq9y2ldr6k3fjrsjscqhaqtzvcth","previous_block":"469de7723eddc90504769cb9e1ef1252c7d84aa0dcdc728d4e3524db923db651","next_block":null,"confirmations":0}}
< {"id":2,"type":"message","data":{"time":1638459043,"height":6576701,"hash":"edd0d8eec73a043615fdd51b76c929f2231418bafc976ca0531d497ab5581ce4","slot":46892752,"epoch":306,"epoch_slot":63952,"slot_leader":"pool166vvvlk496szmtn4uz6wgcq6djryjk70grpap02yxyspjzkr490","size":68826,"tx_count":42,"output":"2196392602738","fees":"10265229","block_vrf":"vrf_vk1kmvctdxmaqz8tp04tsdgqrk7vfmdutmyq782rd6yvz3zlugqmezq2kf3yf","previous_block":"4f4ebd2246a7c9f61624c6eaf09038d36474ba9be788526637a0d68ccb50ad93","next_block":null,"confirmations":0}}
< {"id":2,"type":"message","data":{"time":1638459047,"height":6576702,"hash":"ad9ff078664cc6b56adac63cad3832d9383157d9eb39c742dc4016f54f39a93e","slot":46892756,"epoch":306,"epoch_slot":63956,"slot_leader":"pool1cc76kmtcpf6vht32ya5ke9er74dnpy4jh5qpy4klqwp87ygdsu6","size":58765,"tx_count":35,"output":"3219104538435","fees":"8369852","block_vrf":"vrf_vk182ttxy4dhxyvmkhxd3zac54f5zwqz7hhnvru8prax2udfcsve8dq8m6h68","previous_block":"edd0d8eec73a043615fdd51b76c929f2231418bafc976ca0531d497ab5581ce4","next_block":null,"confirmations":0}}
...

UI explorer

There is an example UI for this project called blockfrost-websocket-link-ui and you can find its hosted version at websocket-link.blockfrost.dev.

API

As its name suggests, blockfrost-websocket-link provides a WebSocket-based API: the client needs to connect to it through a WebSocket. Once the WebSocket connection is correctly established the server is immediately ready to accept commands. The communication (both ways) is JSON encoded: the server expects JSON encoded messages and will respond using the same encoding.

Each command message requires:

  • id: an identifier which is mirrored by the server in the output message; This identifier, while unused by the server, is useful for the client to reconcile output messages to relative input message.
  • command: Command to execute (ESTIMATE_FEE | GET_ACCOUNT_INFO | GET_ACCOUNT_UTXO | GET_BALANCE_HISTORY | GET_BLOCK | GET_SERVER_INFO | GET_TRANSACTION | PUSH_TRANSACTION | SUBSCRIBE_ADDRESS | SUBSCRIBE_BLOCK | UNSUBSCRIBE_ADDRESS | UNSUBSCRIBE_BLOCK )
  • params: optionally a set of parameters, depending on the command.

The structure of an input message is:

{
  id: number | string; // Unique identifier for tracking responses
  "command": string;     // Command
  "params": {            // Parameters object based on the command
    ...
  }
}

General structure of a success response:

{
  id: number | string; // Unique identifier for tracking responses
  "type": "message"
  data: { // payload
    ...
  }
}

Structure of an error response:

{
  id: number | string; // identifier
  "type": "error"
  data: {
    "error": {
      "message": string;
      // These optional properties below are set in case of BlockfrostServerError or BlockfrostClientError
      // thrown by blockfrost-js https://github.com/blockfrost/blockfrost-js?tab=readme-ov-file#error-handling
      "error"?: string;
      "status_code"?: number;
      "name"?: number;
      "url"?: number;
      "body"?: unknown;
    }
  },
}

Request / response commands

For each of these commands the client receives an immediate response

Subscription commands

Each subscription command immediately responds with a confirmation of the subscription status. Upon successful subscription, the client will receive additional messages whenever the subscribed events occur.

When an unsubscribe command is sent, the client receives an immediate response confirming the unsubscribe status. Once successfully unsubscribed, the client will no longer receive messages for those events.

ESTIMATE_FEE

Estimates the minimum fee required for transaction submission based on the linear fee parameters for the current epoch.

Input message:

{
  id: number | string; // identifier
  "command": "ESTIMATE_FEE"
}

Response:

{
  id: number | string;
  type: "message";
  data: {
    //The linear factor for the minimum fee calculation for given epoch (min_fee_a)
    // https://docs.cardano.org/about-cardano/explore-more/parameter-guide/#a-list-of-updatable-protocol-parameters
    lovelacePerByte: number;
  }
}

GET_ACCOUNT_INFO

Retrieves the account details such as current balance, basic staking information, list of used addresses, paginated list of transactions associated with the account (optional) and more.

Input message:

{
  "id": number | string;
  "command": "GET_ACCOUNT_INFO";
  "params": {
    "descriptor": string; // account public key in hex (eg. 6d17587575a3b4f0f86ebad3977e8f7e4981faa863eccf5c1467065c74fe3435943769446dd290d103fb3d360128e86de4b47faea73ffb0900c94c6a61ef9ea2)
    "details": 'basic' | 'tokens' | 'tokenBalances' |'txids' | 'txs';
    "page?": number; // optional, default 1
    "pageSize?": number; // optional, default 20
  }
}

Response:

{
  id: number | string;
  type: "message";
  data {
    balance: string;
    addresses?: {
      change: {
        address: string;
        path: string;
        transfers: number;
        balance?: string;
        sent?: string;
        received?: string;
      }[];
      used: {
        address: string;
        path: string;
        transfers: number;
        balance?: string;
        sent?: string;
        received?: string;
      }[];
      unused: {
        address: string;
        path: string;
        transfers: number;
        balance?: string;
        sent?: string;
        received?: string;
      }[];
    };
    empty: boolean;
    availableBalance: string;
    descriptor: string;
    tokens?: {
      unit: string;
      quantity: string;
      decimals: number;
      name?: string | null;
      ticker?: string | null;
      fingerprint?: string;
    }[];
    history: {
      total: number;               // total transactions
      unconfirmed: number;         // unconfirmed transactions
      transactions?: {             // List of transactions data, available with details set to "txs"
        txUtxos: {                 // https://docs.blockfrost.io/#tag/cardano--transactions/GET/txs/{hash}/utxos
          hash: string;
          inputs: {
            address: string;
            amount: {
              unit: string;
              quantity: string;
              decimals: number;
              name?: string | null;
              ticker?: string | null;
              fingerprint?: string;
            }[];
            tx_hash: string;
            output_index: number;
            data_hash: string | null;
            collateral: boolean;
          }[];
          outputs: {
            address: string;
            amount: {
              unit: string;
              quantity: string;
              decimals: number;
              name?: string | null;
              ticker?: string | null;
              fingerprint?: string;
            }[];
            output_index: number;
            data_hash: string | null;
          }[];
        };
        txData: { // https://docs.blockfrost.io/#tag/cardano--transactions/GET/txs/{hash}
          hash: string;
          block: string;
          block_height: number;
          block_time: number;
          slot: number;
          index: number;
          output_amount: {
            unit: string;
            quantity: string;
            decimals: number;
            name?: string | null;
            ticker?: string | null;
            fingerprint?: string;
          }[];
          fees: string;
          deposit: string;
          size: number;
          invalid_before: string | null;
          invalid_hereafter: string | null;
          utxo_count: number;
          withdrawal_count: number;
          mir_cert_count: number;
          delegation_count: number;
          stake_cert_count: number;
          pool_update_count: number;
          pool_retire_count: number;
          asset_mint_or_burn_count: number;
          redeemer_count: number;
          valid_contract: boolean;
        };
        address: string;
        txHash: string;
        txCbor?: string;
      }[];
      txids?: string[];            // List of transaction ids, available with details set to "txids"
    };
    page: {
      size: number;
      total: number;
      index: number;
    };
    misc: {
      staking: {
        address: string;
        isActive: boolean;
        rewards: string;
        poolId: string | null;
        drep: {
          drep_id: string;
          hex: string;
          amount: string;
          active: boolean;
          active_epoch: number | null;
          has_script: boolean;
        } | null;
      };
    };
  }
}

GET_ACCOUNT_UTXO

Fetches all unspent transaction outputs (UTXOs) for the specified account.

Input message:

{
  "id": number | string;
  "command": "GET_ACCOUNT_UTXO";
  "params": {
    "descriptor": string; // account public key
  }
}

Response:

{
  id: number | string;
  type: "message";
  data: {
    address: string;
    utxoData: {
      tx_hash: string;
      tx_index: number;
      output_index: number;
      amount: {
        unit: string;
        quantity: string;
        decimals: number;
        name?: string | null;
        ticker?: string | null;
        fingerprint?: string;
      }[];
      block: string;
      data_hash: string | null;
    };
    path: string;
    blockInfo: { // https://docs.blockfrost.io/#tag/cardano--blocks/GET/blocks/{hash_or_number}
      hash: string;
      block: string;
      block_height: number;
      block_time: number;
      slot: number;
      index: number;
      output_amount: {
          unit: string;
          quantity: string;
      }[];
      fees: string;
      deposit: string;
      size: number;
      invalid_before: string | null;
      invalid_hereafter: string | null;
      utxo_count: number;
      withdrawal_count: number;
      mir_cert_count: number;
      delegation_count: number;
      stake_cert_count: number;
      pool_update_count: number;
      pool_retire_count: number;
      asset_mint_or_burn_count: number;
      redeemer_count: number;
      valid_contract: boolean;
  };
  }[];
}

GET_BALANCE_HISTORY

Calculates a history of balance changes for an account within a specified date range.

Input message:

{
  "id": number | string;
  "command": "GET_BALANCE_HISTORY";
  "params": {
    "descriptor": string; // account public key
    "groupBy": number;
    "from": number;
    "to": number;
  }
}

Response:

{
  id: number | string;
  type: "message";
  data: {
    time: number;
    txs: number;
    received: string;
    sent: string;
    sentToSelf: string;
    rates?: { [k: string]: number | undefined };
  }[]
}

GET_ADA_HANDLE

Resolves an Ada Handle providing the address holding it.

Input message:

{
  "id": number | string;
  "command": "GET_ADA_HANDLE";
  "params": {
    "name": string; // Ada Handle name
  }
}

Response output message data type:

{
  id: number | string;
  type: 'message';
  data: string | null;
}

GET_BLOCK

Retrieves detailed information about a specific block using its hash or height.

Input message:

{
  "id": number | string;
  "command": "GET_BLOCK";
  "params": {
    "hashOrNumber": number | string; // Block hash or block height
  }
}

Response: Payload contains block data.

{
  id: number | string;
  type: 'message';
  data: {
    hash: string;
    block: string;
    block_height: number;
    block_time: number;
    slot: number;
    index: number;
    output_amount: {
      unit: string;
      quantity: string;
    }
    [];
    fees: string;
    deposit: string;
    size: number;
    invalid_before: string | null;
    invalid_hereafter: string | null;
    utxo_count: number;
    withdrawal_count: number;
    mir_cert_count: number;
    delegation_count: number;
    stake_cert_count: number;
    pool_update_count: number;
    pool_retire_count: number;
    asset_mint_or_burn_count: number;
    redeemer_count: number;
    valid_contract: boolean;
  }
}

GET_TRANSACTION

Returns information about a specified transaction.

Input message:

{
  "id": number | string;
  "command": "GET_TRANSACTION";
  "params": {
    "txId": string; // transaction id
  }
}

Response: Payload contains transaction data.

{
  id: number | string;
  type: "message";
  data: { // https://docs.blockfrost.io/#tag/cardano--transactions/GET/txs/{hash}
    hash: string;
    block: string;
    block_height: number;
    block_time: number;
    slot: number;
    index: number;
    output_amount: {
      unit: string;
      quantity: string;
      decimals: number;
      name?: string | null;
      ticker?: string | null;
      fingerprint?: string;
    }[];
    fees: string;
    deposit: string;
    size: number;
    invalid_before: string | null;
    invalid_hereafter: string | null;
    utxo_count: number;
    withdrawal_count: number;
    mir_cert_count: number;
    delegation_count: number;
    stake_cert_count: number;
    pool_update_count: number;
    pool_retire_count: number;
    asset_mint_or_burn_count: number;
    redeemer_count: number;
    valid_contract: boolean;
  };
}

GET_SERVER_INFO

Fetches general information about the server, such as version, network, and the current block height.

Input message:

{
  "id": number | string;
  "command": "GET_SERVER_INFO";
}

Response:

{
  hostname: string;
  name: string;
  shortcut: string;
  testnet: boolean;
  version: string;
  decimals: number;
  blockHeight: number;
  blockHash: string;
}

Example:

{
  "id": 1,
  "type": "message",
  "data": {
    "hostname": "wslink-backend1",
    "name": "Cardano",
    "shortcut": "ada",
    "testnet": false,
    "version": "2.1.1",
    "decimals": 6,
    "blockHeight": 11085275,
    "blockHash": "e958bfd655844f763e7b6613ffbcf17bde09782bb66efaea67ed49e70261439c"
  }
}

PUSH_TRANSACTION

Submits a transaction to the network.

Input message:

{
  "id": number | string;
  "command": "PUSH_TRANSACTION";
  "params": {
    "txData": string; // CBOR representation of the transaction
  }
}

Response output message:

{
  "id": number | string;
  "type": "message";
  "data": string; // transaction id
}

SUBSCRIBE_ADDRESS

Subscribes to transaction notifications for a specified list of addresses. The event consist of detailed information about a specified transaction, including inputs, outputs and transaction CBOR.

Input message:

{
  "id": number | string;
  "command": "SUBSCRIBE_ADDRESS",
  "params": {
    "addresses": string[], // list of bech32 addresses
    "cbor": true           // optional - whether to include transaction CBOR
  }
}

Confirmation of a subscription status:

{
  "id": number | string;
  "type": "message";
  data: {
    "subscribed": true; // successful subscription confirmation
  };
}

Event:

{
  id: number | string;
  type: "message";
  data: {
    address: string;
    txHash: string;
    txData: { // https://docs.blockfrost.io/#tag/cardano--transactions/GET/txs/{hash}
      hash: string;
      block: string;
      block_height: number;
      block_time: number;
      slot: number;
      index: number;
      output_amount: {
        unit: string;
        quantity: string;
        decimals: number;
        name?: string | null;
        ticker?: string | null;
        fingerprint?: string;
      }[];
      fees: string;
      deposit: string;
      size: number;
      invalid_before: string | null;
      invalid_hereafter: string | null;
      utxo_count: number;
      withdrawal_count: number;
      mir_cert_count: number;
      delegation_count: number;
      stake_cert_count: number;
      pool_update_count: number;
      pool_retire_count: number;
      asset_mint_or_burn_count: number;
      redeemer_count: number;
      valid_contract: boolean;
    };
    txUtxos: {
      hash: string;
      inputs: {
        address: string;
        amount: {
          unit: string;
          quantity: string;
          fingerprint?: string; // lovelace has no fingerprint
          decimals: number;
          ticker?: string | null;
          name?: string | null;
        }[];
        tx_hash: string;
        output_index: number;
        data_hash: string | null;
        collateral: boolean;
      }[];
      outputs: {
        address: string;
        amount: {
          unit: string;
          quantity: string;
          fingerprint?: string; // lovelace has no fingerprint
          decimals: number;
          ticker?: string | null;
          name?: string | null;
        }[];
        output_index: number;
        data_hash: string | null;
      }[];
    };
    txCbor?: string;
  }[];
}

SUBSCRIBE_BLOCK

Subscribes to notifications for each new block added to the blockchain.

Input message:

{
  "id": number | string;
  "command": "SUBSCRIBE_BLOCK";
}

Confirmation of a subscription status:

{
  id: number | string;
  "type": "message";
  data: {
    "subscribed": true; // successful subscription confirmation
  };
}

Event message:

{
  id: number | string;
  type: 'message';
  data: {
    // https://docs.blockfrost.io/#tag/cardano--blocks/GET/blocks/latest
    hash: string;
    block: string;
    block_height: number;
    block_time: number;
    slot: number;
    index: number;
    output_amount: {
      unit: string;
      quantity: string;
    }
    [];
    fees: string;
    deposit: string;
    size: number;
    invalid_before: string | null;
    invalid_hereafter: string | null;
    utxo_count: number;
    withdrawal_count: number;
    mir_cert_count: number;
    delegation_count: number;
    stake_cert_count: number;
    pool_update_count: number;
    pool_retire_count: number;
    asset_mint_or_burn_count: number;
    redeemer_count: number;
    valid_contract: boolean;
  }
}

UNSUBSCRIBE_ADDRESS

Unsubscribes from transaction notifications for addresses.

Input message:

{
  "id": number | string;
  "command": "UNSUBSCRIBE_ADDRESS"
}

Output message:

{
  id: number | string;
  data: {
    "subscribed": false; // successful un-subscription confirmation
  },
  "type": "message"
}

UNSUBSCRIBE_BLOCK

Unsubscribes from block event notifications.

Input message:

{
  "id": number | string;
  "command": "UNSUBSCRIBE_BLOCK"
}

Confirmation of a subscription status:

{
  id: number | string;
  type: "message";
  data: {
    "subscribed": false; // successful un-subscription confirmation
  };
}