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

Return to the regular view of this page.

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.