Skip to main content

Subscribe Account Balance

Watchtower supports subscribing to streams of data from Hedera hashgraph. Subscribing to an account balance is one of its every growing features.

Payload Static Reference
export interface AccountID {  'accountNum'?: (number | string | Long);  'realmNum'?: (number | string | Long);  'shardNum'?: (number | string | Long);}export interface GetAccountBalanceQuery {  'cryptogetAccountBalance':{    header:{        responseType: number;    },    accountID: AccountID;  }}export enum PollingIntervals {  '1s' = 1000,  '5s' = 5000,  '10s' = 10000,  '30s' = 30000,}export interface SubscribeAccountBalancePayload {  subscribe: string;  body: GetAccountBalanceQuery;  pollingInterval?: PollingIntervals;}const accountBalanceRoute = `/proto.CryptoService/cryptoGetBalance`;const payloadBody: GetAccountBalanceQuery = {  cryptogetAccountBalance: {    header: {      responseType: 0,    },    accountID: {      accountNum: accountNum,      realmNum: '0',      shardNum: '0',    },  },}const payload: SubscribeAccountBalancePayload = {  subscribe: accountBalanceRoute,  body: payloadBody,  pollingInterval: '5s', // if not set, default is 10s}
Payload breakdown

To subscribe this stream, our payload should match reference protobuf definition.

View Definition
message CryptoGetAccountBalanceQuery {    /**     * Standard info sent from client to node, including the signed payment, and what kind of     * response is requested (cost, state proof, both, or neither).     */    QueryHeader header = 1;    oneof balanceSource {        /**         * The account ID for which information is requested         */        AccountID accountID = 2;        /**         * The account ID for which information is requested         */        ContractID contractID = 3;    }}

By looking into the defination, we can see that we need to provide a oneof balanceSource that is cryptogetAccountBalance, and either AccountID or ContractID to get the balance.

For forming the payload, we should also be aware of basic_types.proto to construct [accountID | contractID].

View Definition
/** * An account, and the amount that it sends or receives during a cryptocurrency or token transfer. */message AccountID {    /**     * The shard number (nonnegative)     */    int64 shardNum = 1;    /**     * The realm number (nonnegative)     */    int64 realmNum = 2;    /**     * The account number unique within its realm which can be a non-negative integer, an alias public key or an EVM address.     * For any AccountID fields in the query response, transaction record or transaction receipt only accountNum will     * be populated.     */    oneof account {        /**         * A non-negative account number unique within its realm         */        int64 accountNum = 3;        /**         * The public key bytes to be used as the account's alias. The public key bytes are the result of serializing         * a protobuf Key message for any primitive key type. Currently only primitive key bytes are supported as an alias         * (ThresholdKey, KeyList, ContractID, and delegatable_contract_id are not supported)         *         * May also be the ethereum account 20-byte EVM address to be used initially in place of the public key bytes. This EVM         * address may be either the encoded form of the shard.realm.num or the keccak-256 hash of a ECDSA_SECP256K1 primitive key.         *         * At most one account can ever have a given alias and it is used for account creation if it         * was automatically created using a crypto transfer. It will be null if an account is created normally.         * It is immutable once it is set for an account.         *         * If a transaction auto-creates the account, any further transfers to that alias will simply be deposited         * in that account, without creating anything, and with no creation fee being charged.         *         * If a transaction lazily-creates this account, a subsequent transaction will be required containing the public key bytes         * that map to the EVM address bytes. The provided public key bytes will then serve as the final alias bytes.         */        bytes alias = 4;    }}/** * The ID for a smart contract instance  */message ContractID {    /**     * The shard number (nonnegative)     */    int64 shardNum = 1;    /**     * The realm number (nonnegative)     */    int64 realmNum = 2;    oneof contract {        /**        * A nonnegative number unique within a given shard and realm        */        int64 contractNum = 3;        /**        * The 20-byte EVM address of the contract to call.         *         * Every contract has an EVM address determined by its <tt>shard.realm.num</tt> id.        * This address is as follows:        *   <ol>        *     <li>The first 4 bytes are the big-endian representation of the shard.</li>        *     <li>The next 8 bytes are the big-endian representation of the realm.</li>        *     <li>The final 8 bytes are the big-endian representation of the number.</li>        *   </ol>          *         * Contracts created via CREATE2 have an <b>additional, primary address</b> that is         * derived from the <a href="https://eips.ethereum.org/EIPS/eip-1014">EIP-1014</a>         * specification, and does not have a simple relation to a <tt>shard.realm.num</tt> id.         *         * (Please do note that CREATE2 contracts can also be referenced by the three-part         * EVM address described above.)        */        bytes evm_address = 4;    }}

All we need is to build a payload as below:

import React, { useEffect, useState } from "react";import io from 'socket.io-client';function StreamingInit () {  // Make sure you match the url for testnet/mainnet   const demoAccountNum = `98`;  const watchtowerURL = `wss://<WATCHTOWER_URL>?api_key=<ARKHIA_API_KEY>`;  const socket = io(watchtowerURL);  const [socketStatus, setSocketStatus] = useState(``);  const [socketConnect, setSocketConnect] = useState(false);  // Available services in page 2 of these docs  const subscribeAccountBalanceRoute = `/proto.CryptoService/cryptoGetBalance`;  const getSubscriptionPayload = (accountNum: string) => {      const subscriptionPayload = {          subscribe: subscribeAccountBalanceRoute,          body: {          cryptogetAccountBalance: {            header: {              responseType: 0,            },            accountID: {              accountNum: accountNum,              realmNum: '0',              shardNum: '0',            },          },        },        pollingInterval: '5s', // polling interval is optional      };      return subscriptionPayload;  };  const subscribeAccountBalance = () => {      const requestPayload = getSubscriptionPayload(demoAccountNum);      socket.emit(`subscribe`, requestPayload, (msg: any) => {                     console.log(`Account Balance streaming down...`);          socket.on(msg.listeners.data, function (message: any) {              console.log(message)          });          socket.on(msg.listeners.error, function (message: any) {              setSocketError(message);              console.log(`Error`);              console.log(message)          });      });  };  useEffect(() => {      socket.on(`connect`, () => {          setSocketConnect(true);          socket.on(`status`, (msg) => {              console.log(`status`, msg);              setSocketStatus(`Watchtower is successfully connected`);          });          socket.emit(`list-services`, (services: any) => {              console.log(`Listing available services`, services);          });      });      socket.on(`disconnect`, (msg: any) => {          setSocketConnect(false);          setSocketStatus(`Watchtower is disconnected.`)          console.log(`Disconnected.`)      });      socket.on(`error`, (message: any) => {          setSocketConnect(false);          console.error(message);          setSocketStatus(message)      })  }, [socket]);  return (      <>          <div>              {                  socketStatus && (                      <>                          <h6>Watchtower status</h6>                          <div>{socketStatus}</div>                      </>                  )              }              {                  socketConnect &&  (                      <>                         <button onClick={subscribeAccountBalance}>                      </>                  )              }          </div>      </>  );}export default StreamingInit;