This the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Developers

We have collected a series of helpfull articles how blockchain protocol developers could use Heat Wallet as way for their community members to easily access on and off chain functionality

We welcome developers to expand and improve the platform.

Blockchain integration development focusses on two parts:

1 - Server Development

The server indexes, caches, and monitors public blockchain info. New blockchain protocol support is added through plugins.

Plugins are written in Javascript/Nodejs, plugins translate individual blockchain API data to a standardized format ready for consumption by the indexing server.

To support the spy on blockchain address feature for a specific blockchain all that is needed is a server plugin as the spy on feature uses only server data.

1.1 - Add your Coin

For the server to be able to index a blockchain, a plugin is needed that translates blockchain RPC/API requests and responses.

The following info must be obtained to fully index any blockchain:

To send transactions to the blockchain network plugins must also be able to send raw transactions and implement:

Plugin SDK

The Plugin SDK is a module we’ve developed to assist in the creation and local testing of new blockchain plugins.

The SDK is a NodeJs/npm package that gives you a skeleton implementation of a complete plugin with unit tests.

Read more about the plugin sdk

1.2 - Plugin SDK

Plugin sdk was created to assist developers in creating server plugins for Heat mobile wallet app server.

Quick overview

To add indexer support for (part of) a blockchain you’d best use the Plugin SDK.

The steps involved in adding support for a feature are as follows.

  1. You fork the plugin sdk repo

Now for each feature.

  1. You write code that translates parameters to a URL
  2. You translate the response from the server to a format understood by the indexer
  3. You write any extra tests apart from the existing basic tests available

Finally

  1. You run a build script and commit the code to github.com
  2. You tell us about your plugin by opening an issue on our issue tracker
  3. We review your plugin and integrate it when it meets our quality standards

These features are currently supported in our indexer servers.

Server plugins can choose to support one, some or all of these features as several plugins can be combined to construct a complete working blockchain indexer.

Step by step guide

Follow the steps below to get started with writing your first plugin.

Fork the SDK

Detailed steps on how to fork a repo on GitHub are available here.

  1. On Github, navigate to heatcrypto/heat-server-sdk
  2. In the top-right corner of the page, click Fork

Make sure the name of your fork contains the name of the blockchain and optionally your name or organization name.

In case you are creating more than one plugin you will notice that GitHub does not allow the creation of multiple forks of the same repository under one single account. From the website. To get around this restriction follow these instructions.

Rename your fork

To rename your forked repository from heat-server-sdk to your desired name please follow the instructions below.

  1. On GitHub, navigate to the main page of the repository.
  2. Under your repository name, click Settings.
  3. Under the Repository Name heading, type the new name of your repository.
  4. Click Rename.

Best practice is to name your repo heat-server-BLOCKCHAIN where BLOCKCHAIN is the chain you are creating the plugin for. If that name is taken or if you wish to include your name in the repo please follow this pattern heat-server-BLOCKCHAIN-YOURNAME (for example heat-server-ripple-mike).
Naming plugins this way ensures maximum reusability and discoverability.

Code your feature

Each feature is implemented in what we call a module, all modules live in the src/modules directory.

Modules all expose one function, each specific module must adhere to a specific signature which is shared between your plugin and our indexer server.

As an example lets look at what the transaction status module looks like before we have done any coding.

import { 
  TransactionStatusParam, TransactionStatusResult, 
  tryParse, CallContext, ModuleResponse } from 'heat-server-common'

export async function transactionStatus(
  context: CallContext, 
  param: TransactionStatusParam): Promise<ModuleResponse<TransactionStatusResult>> {
  try {
    const { req, protocol, host, logger } = context
    const { blockchain, assetType, addrXpub, transactionId } = param
    const url = `${protocol}://${host}/api/GET-TRANSACTION-STATUS?transactionId=${transactionId}`;
    const json = await req.get(url);
    const data = tryParse(json, logger);
    
    const confirmations: number = 0;
    const isAccepted: boolean = false;
    
    return {
      value: {
        confirmations,
        isAccepted,
      },
    };
  } catch (e) {
    return {
      error: e.message,
    };
  }
}

heat-server-common

All plugins make use of heatcrypto/heat-server-common as it allows sharing of code between all plugins and our indexing server.

CallContext

Each module receives a CallContext as its first argument. The CallContext is provided by the indexing server.

When running unit tests you have to provide the CallContext yourself. This however is basic as all you have to do is call the createContext(label: string) function in each of your tests.

/**
 * Each module is provided with a CallContext each time it is invoked
 */
interface CallContext {

  /**
   * Network protocol (http or https)
   */
  protocol: string;

  /**
   * Host (plus optional port) of api server
   */
  host: string;

  /**
   * Main logger, created as prefixlogger with prefix that 
   * identifies the plugin
   */
  logger: LoggerService;

  /**
   * Middleware that deals with address conversions and 
   * possible other future topics
   */
  middleWare?: ExplorerMiddleware;

  /**
   * A configured instance of MonitoredRequest is provided, 
   * this does GET and POST requests
   */
  req: MonitoredRequest;
}

Call Parameters

Modules are not much good if you cant pass parameters to them. That’s where the second parameter to each module comes into play. Each module has its own interface for their parameter as well as for their result. All these interfaces can be found listed here or look in the right hand side menu on this page at each feature and look for Plugin Input.

Typically many more parameters are provided to your module than you actually need, the reason for this stems from the fact that these parameters (like which blockchain or assetType) are used much higher in the call hierarchy in the indexing servers.

API url

Its your task to construct the GET url to the blockchain api server.

const url = `${protocol}://${host}/api/GET-TRANSACTION-STATUS?transactionId=${transactionId}`;

Its crucial to use the protocol and host provided by the CallContext. Otherwise your plugin cannot be used in parallel when more than one blockchain API server is available.

Download API response

Once we have the URL for our request we can download this data with either GET or POST and parse the response (if its json).

The tryParse method provided by heat-server-common is the preferred and advised way to parse JSON. Using this method ensures proper error handling and logging.

The req object (MonitoredRequest) provided by CallContext should be used for all your GET and POST requests, this allows us to tune the specifics of the underlying HTTP client.

const json = await req.get(url);
const data = tryParse(json, logger);

To do POST requests are used as follows.

const json = await req.post(url, { form: { transactionBytes: transactionHex } }, [200]);
const data = tryParse(json, logger);

Translate response

Finally, you translate the response from the blockchain API server to the expected result by the indexing server. The details of this result can be found in the Result interface as can be seen in the example above.

Test your code

To test your code we use unit tests which have sensible defaults and can run be run as is.

Let’s look at the unit test for the transaction status feature we just coded. Below is the code as it is provided to you by the SDK you forked in the previous steps.

import * as chai from 'chai';
const { isObject, isNumber, isBoolean } = chai.assert
import 'mocha';
import { createContext } from './test_config'
import { transactionStatus } from '../src/modules/transaction_status';
import { Blockchains, AssetTypes } from 'heat-server-common';

describe('Transaction Status', () => {
  it('should work', async () => {
    const blockchain: Blockchains = Blockchains.ETHEREUM
    const assetType: AssetTypes = AssetTypes.NATIVE
    const addrXpub: string = '0x12345'
    const transactionId: string = '0x67890'
    let resp = await transactionStatus(createContext('Transaction'), {
      blockchain, assetType, addrXpub, transactionId
    })
    isObject(resp)
    let result = resp.value
    isObject(result)
    isNumber(result.confirmations)
    isBoolean(result.isAccepted)
  });
});

As can be seen, we have set up the mocha test framework and are using chai assertion library. While a basic test is provided you should spend time and code your own.

Configure your test

Before you can run your tests you need to configure them by opening test/test_config.ts and set your protocol and host values.

export const testConfig = {
  protocol: 'http',
  host: 'localhost:3000'
}

Run your tests

To run your tests on the command line run npm run test.

Deploying your plugin

When you have completed writing your module code and unit tests its time to prepare your plugin for deployment.

To do so open src/explorer.ts and at a minimum set the ID value to a globally unique identifier that identifies your plugin.

We strongly advise you using an id that is made up of the last part of your repo name. The part after heat-server- specifically.
So if your plugin is named heat-server-bitcoin-mike please use it as an ID bitcoin-mike

Optionally you can comment out or add any modules you did or did not implement in the modules variable.

It’s good practice to remove any module you did not implement as it helps the indexing server identify when it’s trying to call a feature that is not implemented in your plugin.

Building your code

Finally, we build the plugin by running a script on the command line. Open a command prompt at your plugin root and run npm run prepublish, this will transpile your typescript into javascript and creates the dist folder.

Commit build to github.com

For the indexer server to directly load your plugin code from Github you should add the compiled code in the dist folder to your repo and push these changes to Github.

To do so open .gitignore and remove the line that says /dist.

Now on the commandline add, commit and push the code in /dist.

$ git add dist
$ git commit -m "Add dist to git"
$ git push origin master

Enable your plugin in Heat Wallet

Now that you have created your plugin we will review it before making it available on our indexer platform.

Please create a new issue at https://github.com/heatcrypto/heat-server-sdk/issues and cleary state you request a review and include your plugin repo on github.

1.3 - Fork SDK (non standard)

When creating one single fork of the SDK GitHub will allow this through their website. When we however create more than one fork of one repository (which we do every time you clone heat-server-sdk) some other steps are necessary.

In this example we assume to create a plugin for Ripple named heat-server-ripple. Wherever it says heat-server-ripple this should be updated to the name you had in mind for your plugin.

Step 1 - Clone SDK to your local machine

git clone https://github.com/heatcrypto/heat-server-sdk.git heat-server-ripple
cd heat-server-ripple

Step 2 - Create a new empty repo on Github

Go to your Github account on github.com, select the small + in the top right and select New repository.

Enter the name for your new repo and optional description.

Remember your GitHub account name and your new repo name. For this example, we assume the following user name satoshi and repo name heat-server-ripple.

git remote -v

This should give the following output.

origin https://github.com/heatcrypto/heat-server-sdk.git (fetch)
origin https://github.com/heatcrypto/heat-server-sdk.git (push)

Step 4 - Rename origin to upstream and add our new empty repo as the origin

Replace the GitHub name satoshi and repo name heat-server-ripple with your name and repo name.

git remote rename origin upstream
git remote add origin https://github.com/satoshi/heat-server-ripple.git
git remote -v

This should give the following output (updated with your name and repo name of course)

origin https://github.com/satoshi/heat-server-ripple.git (fetch)
origin https://github.com/satoshi/heat-server-ripple.git (push)
upstream https://github.com/heatcrypto/heat-server-sdk.git (fetch)
upstream https://github.com/heatcrypto/heat-server-sdk.git (push)

Step 5 - Push from your local repo to your new remote one

git push -u origin master

1.4 - Network Status

Network status is important for several reasons and should return real-time information about the network.

Plugin Input

Please see heat-server-common/src/constants.ts for type definitions

interface { 
  /**
   * Enum of blockchain identifiers
   */ 
  blockchain?: Blockchains
}

Plugin Output

interface {
  /**
   * The height of the last block
   */
  lastBlockHeight: number;

  /**
   * The last block timestamp in UTC format
   */
  lastBlockTime: Date;

  /**
   * The unique block id (hex encoded)
   */
  lastBlockId: string;
}

1.5 - Network Fee

Network fee information is used to advise wallet users as to which fee they should include in their transactions based on network load.

While this form of network fee is still part of the Plugin SDK and used actively. It was kind of superseded by more specialized services that advise on slow, normal, fast fee values based on real-time blockchain activity.

Examples of varying fees based on network usage are:

  • Bitcoin and most other Utxo based networks satoshi per byte measure.
  • Ethereum gas price

Plugin Input

Please see heat-server-common/src/constants.ts for type definitions

interface { 
  /**
   * Enum of blockchain identifiers
   */ 
  blockchain?: Blockchains
}

Plugin Output

interface {
  /**
   * Gas price in wei units
   */
  gasPriceWei?: string;

  /**
   * Recommended number of satoshis per byte
   */
  satByte?: string;
}

1.6 - Token Discovery

Token discovery is the process of identifying and returning information on which supported tokens belong to an address. Several token types exists for several different blockchains.

We use the term token to describe any kind of unit that could be held by an address. One such example would be the native currency on a blockchain (each Bitcoin address has at least the Bitcoin token). Other chains support more token types, Ethereum addresses for example can contain many different erc20 tokens.

Plugin Input

Please see heat-server-common/src/constants.ts for type definitions

interface {
  /**
   * Enum of blockchain identifiers
   */   
  blockchain: Blockchains,

  /**
   * Enum of asset or token types
   */   
  assetType: AssetTypes,

  /**
   * Address or public key
   */   
  addrXpub: string,
}

Plugin Output

interface {
  /**
   * Result is an array
   */
  [index: number]: {

    /**
     * Unique identifier (erc20 contract addr, or '0' for native currency)
     */
    assetId: string;

    /**
     * Enum of asset or token types
     */   
    assetType: number;

    /**
     * Amount/value in smallest unit on blockchain (satoshi, wei, etc)
     */  
    value: string;

    /**
     * Indicates the address received atleast one transaction
     */
    exists: boolean;
  }
}

1.7 - Balance Lookup

Look up the current balance for a token on a blockchain network.

Plugin Input

Please see heat-server-common/src/constants.ts for type definitions

interface {
  /**
   * Enum of blockchain identifiers
   */   
  blockchain: Blockchains,

  /**
   * Enum of asset or token types
   */   
  assetType: AssetTypes,

  /**
    * Unique identifier (erc20 contract addr, or '0' for native currency)
    */
  assetId: string;

  /**
   * Address or public key
   */   
  addrXpub: string,
}

Plugin Output

interface {
  /**
    * Amount/value in smallest unit on blockchain (satoshi, wei, etc)
    */  
  value: string;

  /**
    * Indicates the address received atleast one transaction
    */
  exists: boolean;
}

1.8 - Event Lookup

Events are considered everything that can happen to or with an address.
  1. events happen chronologically
  2. events apply to an address
  3. an address is either the sender or receiver of an event
  4. different blockchains support different events
  5. event payloads are based on event type payload interface
  6. a single transaction can produce multiple events

Based on Transactions

Event lookup is mostly based on processing transactions. Plugin code processes lists of transactions and returns lists of events.

Examples of events

  1. funds are send
  2. funds are received
  3. an utxo was spend
  4. an utxo was received
  5. fee was paid
  6. buy order was placed on dex
  7. sell order was placed on dex
  8. balance was staked

Plugin Input

Please see heat-server-common/src/constants.ts for type definitions

interface {
  /**
   * Enum of blockchain identifiers
   */   
  blockchain: Blockchains,

  /**
   * Enum of asset or token types
   */   
  assetType: AssetTypes,

  /**
   * Unique identifier (erc20 contract addr, or '0' for native currency)
   */
  assetId: string;

  /**
   * Address or public key
   */   
  addrXpub: string,

  /**
   * Zero indexed, events are ordered chronologically. 
   * The zero'd event is the newest
   */ 
  from: number,

  /**
   * Events in range [from, to] are returned
   */ 
  to: number,

  /**
   * Optional. Minimal indicates no event details are returned, only event identifiers
   */
  minimal?: boolean,
}

Plugin Output

Please see heat-server-common/src/event-builders.ts for type definitions

interface {

  /**
   * Result is an array
   */
  [index: number]: {

    /**
     * Unix timestamp (ms since epoch)
     */
    timestamp: number;

    /**
     * The tranasaction id
     */
    sourceId: string;

    /**
     * Enum of sourcetypes, always 0=TRANSACTION at this stage
     */
    sourceType: SourceTypes;

    /**
     * Number of confirmations. 
     * 0 means unconfirmed/in mem-pool
     * 1 in latest block
     * 2 in previous block
     * etc ..
     */
    confirmations: number;

    /**
     * Lists events related to the input address, event contents always 
     * are relative to the input address. If for instance this transaction 
     * was send by the input address, the address in the event will be the 
     * recipient and vice versa.
     */
    events: Array<{

      /**
       * Enum event type (see Event Types)
       */
      type: EventTypes;

      /**
       * Enum of asset or token types
       */   
      assetType: AssetTypes;

      /**
       * Unique identifier (erc20 contract addr, or '0' for native currency)
       */
      assetId: string;

      /**
       * Event data payload which differs based on the event type (see Event Types)
       */
      data: EventStandardTypeData | EventFeeTypeData | EventOrderTypeData |
            EventLeaseBalanceTypeData | EventMessageTypeData | 
            etc..;
    }>;  
  }
}

1.9 - Event Types

Background reference of interfaces referenced in event lookup
enum SourceTypes {
  TRANSACTION = 0,
}

enum EventTypes {
  EVENT_SEND = 1,
  EVENT_RECEIVE = 2,
  EVENT_OUTPUT = 3,
  EVENT_INPUT = 4,
  EVENT_FEE = 5,
  EVENT_BUY_ORDER = 6,
  EVENT_SELL_ORDER = 7,
  EVENT_CANCEL_BUY_ORDER = 8,
  EVENT_CANCEL_SELL_ORDER = 9,
  EVENT_LEASE_BALANCE = 10,
  EVENT_MESSAGE_SEND = 11,
  EVENT_MESSAGE_RECEIVE = 12,
}

interface EventStandardTypeData {
  value: string;
  addrXpub: string;
  publicKey: string;
  alias: string;
  n: number;
}

interface EventFeeTypeData {
  value: string;
}

interface EventOrderTypeData {
  currencyType: AssetTypes;
  currencyId: string;
  quantity: string;
  price: string;
}

interface EventLeaseBalanceTypeData {
  period;
  addrXpub: string;
  publicKey: string;
  alias: string;
}

interface EventMessageTypeData {
  addrXpub: string;
  publicKey: string;
  alias: string;
  isText: boolean;
  message:
    | string
    | {
        data: string;
        nonce: string;
      };
}

1.10 - Utxo Lookup

Only for Utxo based blockchains, the Utxo lookup exists. The goal is to return unspent outputs for an address.

Plugin Input

Please see heat-server-common/src/constants.ts for type definitions

interface {
  /**
   * Enum of blockchain identifiers
   */   
  blockchain: Blockchains,

  /**
   * Enum of asset or token types
   */   
  assetType: AssetTypes,

  /**
   * Unique identifier (erc20 contract addr, or '0' for native currency)
   */
  assetId: string;

  /**
   * Address or public key
   */   
  addrXpub: string,    
}

Publin Output

interface {

  /**
   * Result is an array
   */
  [index: number]: {

    /**
     * The utxo value in satoshi
     */
    value: string;

    /**
     * Transaction id
     */
    txid: string;

    /**
     * Utxo index
     */
    vout: number;

    /**
     * Number of confirmations
     */
    confirmations: number;

    /**
     * Utxo locktime (or 0 if none)
     */
    lockTime: number;

    /**
     * For unconfirmed transactions this will return 0
     */
    height: number;  
  }
}

1.11 - Transaction Status

To keep track of transactions and their status (confirmations or in mem-pool).

Plugin Input

Please see heat-server-common/src/constants.ts for type definitions

interface {
  /**
   * Enum of blockchain identifiers
   */   
  blockchain: Blockchains;

  /**
   * Enum of asset or token types
   */   
  assetType: AssetTypes;

  /**
   * Address or public key
   */   
  addrXpub: string;

  /**
   * Unique transaction identifier
   */
  transactionId: string;
}

Public Output

interface {

  /**
   * Number of confirmations, returns 0 for unconfirmed transactions
   */
  confirmations: number;

  /**
   * In case of unconfirmed transactions this indicates if the transaction 
   * resides in the mem-pool or was rejected
   */
  isAccepted: boolean;  
}

1.12 - Alias Lookup

For blockchains that support address aliases.

Supported are:

(with more planned)

There are two plugin interfaces here, forward and reverse resolve.

Plugin Input

Please see heat-server-common/src/constants.ts for type definitions

/**
 * Resolve alias
 */
interface {
  /**
   * Enum of blockchain identifiers
   */   
  blockchain: Blockchains;

  /**
   * Enum of asset or token types
   */   
  assetType: AssetTypes;

  /**
   * Alias
   */   
  alias: string;
}

/**
 * Reverse resolve alias
 */
interface {
  /**
   * Enum of blockchain identifiers
   */   
  blockchain: Blockchains;

  /**
   * Enum of asset or token types
   */   
  assetType: AssetTypes;

  /**
   * Address or public key
   */   
  addrXpub: string;
}

Plugin Output

/**
 * Resolve alias
 */
interface {
  addrXpub: string;
  isPermanent: boolean;
}

/**
 * Reverse resolve alias
 */
interface {
  alias: string;
  isPermanent: boolean;
}

1.13 - Public Key Lookup

Not required for most blockchains. Returns the publickey for account based blockchain address.

Plugin Input

Please see heat-server-common/src/constants.ts for type definitions

interface {
  /**
   * Enum of blockchain identifiers
   */   
  blockchain: Blockchains;

  /**
   * Address or public key
   */   
  addrXpub: string;
}

Plugin Output

interface {
  publicKey: string;
}

1.14 - Broadcast Transaction

Broadcasts a transaction to the blockchain network.

Plugin Input

Please see heat-server-common/src/constants.ts for type definitions

interface {
  /**
   * Enum of blockchain identifiers
   */ 
  blockchain: Blockchains;

  /**
   * Enum of asset type identifiers
   */ 
  assetType: AssetTypes;

  /**
   * Raw transaction hex encoded
   */
  transactionHex: string;
}

Plugin Output

interface {
  /**
   * Error message as returned from the blockchain node, if any.
   * errorMessage and transactionId are mutually exclusive
   */
  errorMessage?: string;

  /**
   * Transaction id of broadcasted transaction, return of a transaction id also indicates 
   * successfull broadcast. This does not indicate the transaction will end on the blockchain
   * just that its initially accepted by the blockchain node.
   * errorMessage and transactionId are mutually exclusive
   */
  transactionId?: string;
}

2 - App Development

Blockchain support in the app consists of address, key, cryptography, and transaction building support.

The mobile app is available for iPhone and Android.

Several methods are applied to provide app integrations:

  • Embedded NodeJs Engine
  • Embedded wallet-core Engine
  • Native Dart, Java, Swift, and C code

At this point there exists no 100% open to the public method of adding support for your protocol, we do however are open to working with developers. With just a handful of methods you could add address and transaction support through NodeJS, we will be more than happy to work with you to add such to the wallet.