Plugins

Plugins offer useful features to the application and external services, which are part of the off-chain logic of the blockchain application. In other words, a plugin will never change the state of the blockchain itself, contrary to the modules, which hold the on-chain logic of the blockchain application.

When to create a plugin

Plugins are able to perform the following:

  • Search the blockchain data.

  • Aggregate the blockchain data.

  • Provide a UI for the blockchain application.

  • Automate the blockchain logic, such as automatically sending transactions.

  • Add a proxy to the application interfaces.

For a more practical guide how to create a new plugin, check out the guide Creating a new plugin.

If you wish to view an example of a fully implemented plugin, check out the following examples:

Adding a plugin to the application

Plugins need to be registered to become available in the application. If the application was bootstrapped with Lisk Commander, they are registered in the file src/app/plugins.ts.

Example: How to register a plugin with the application in plugins.ts
import { Application } from 'lisk-sdk';
import { SomePlugin } from "some-plugin"; (1)
import { OtherPlugin } from "other-plugin";

const customConfig = {
    port: 5555,
    host: "127.0.0.1"
};

export const registerPlugins = (app: Application): void => {
    app.registerPlugin(SomePlugin); (2)
    app.registerPlugin(OtherPlugin, customConfig); (3)
};
1 Import the plugin from an NPM package or from a local path.
2 Add this line to register the plugin to the application.
3 Optional: provide a custom configuration for the plugin. If no custom config option is provided, the plugin will use the default config options, defined in Plugin defaults.

Plugin anatomy

plugin-diagram

The plugin class

All custom plugins must extend the BasePlugin exposed by the SDK. The interface for the BasePlugin is described in the Lisk Framework reference.

const { BasePlugin } = require("lisk-sdk");

class ForgerPlugin extends BasePlugin {

}

Plugin Alias

The unique identifier of the plugin.

It is used as prefix in the alias of events and actions, and as a key label to to add the properties of the Plugin defaults to the application configuration.

Example: Alias of the forger plugin
public static get alias(): string {
    return 'forger';
}

Plugin info

Plugin meta information.

Example: Info of the forger plugin
public static get info(): PluginInfo {
    return {
        author: packageJSON.author,
        version: packageJSON.version,
        name: packageJSON.name,
    };
}

Plugin defaults

The configuration schema for this plugin.

After registering the plugin with the application, it can be configured in the application configuration, using the defined schema here. The configuration options can then be accessed in the plugin under the variable this.options.

Example: Config schema of the Dashboard plugin
get defaults() {
    return {
        $id: '#/plugins/lisk-dashboard/config',
        type: 'object',
        properties: {
            applicationName: {
                type: 'string',
                description: 'Application name to be shown near Logo',
            },
            applicationUrl: {
                type: 'string',
                format: 'uri',
                description: 'URL to connect',
            },
            port: {
                type: 'integer',
                minimum: 1,
                maximum: 65535,
            },
            host: {
                type: 'string',
                format: 'ip',
            },
        },
        required: [],
        default: {
            applicationUrl: 'ws://localhost:8080/ws',
            port: 4005,
            host: '127.0.0.1',
            applicationName: 'Lisk',
        },
    }
};

Interfaces

Plugins can expose interfaces (Actions and Events), which allow other plugins and external tools to interact with the plugin.

View the "Interfaces" section of the Communication page to see an overview of the different interfaces and their accessibility in modules, plugins, and external services.

Actions

Actions are functions which can be invoked via Remote-Procedure-Calls (RPC) by plugins and external services, to request data from the plugin.

Example: Actions of the forger plugin
public get actions(): ActionsDefinition {
    return {
        getVoters: async () =>
            controllers.voters.getVoters(this._channel, this.codec, this._forgerPluginDB),
        getForgingInfo: async () =>
            controllers.forgingInfo.getForgingInfo(this._channel, this.codec, this._forgerPluginDB),
    };
}

Events

Events are published by the plugin on relevant occasions. Other plugins and external services can subscribe to these events and as a result, they will be notified immediately every time a new event is published.

Example: Events of the forger plugin
public get events(): EventsDefinition {
    return ['block:created', 'block:missed'];
}

Execution logic

load()

The load() method will be invoked by the controller to load the plugin. It contains the plugin logic that is executed when the plugin is loaded in the application.

It can be used to retrieve, mutate, store and/or publish data in a specific manner, depending on the purpose of the plugin.

The channel, which is available inside of the load() function, allows access to the RPC endpoints of a Lisk node in order to subscribe to events or to invoke certain actions within the application to retrieve the desired data.

The following variables are accessible in the load() function:

Example: load() function of the forger plugin
public async load(channel: BaseChannel): Promise<void> {
    // Merge custom plugin configuration with default options
    const options = objects.mergeDeep({}, config.defaultConfig.default, this.options) as Options;
    this._channel = channel;

    this._forgerPluginDB = await getDBInstance(options.dataPath);

    this._channel.once('app:ready', async () => {
        // Fetch and set forger list from the app
        await this._setForgersList();

        // Fetch and set transactions fees
        await this._setTransactionFees();

        // Sync the information
        this._syncingWithNode = true;
        await this._syncForgerInfo();
        this._syncingWithNode = false;

        // Listen to new block and delete block events
        this._subscribeToChannel();
    });
}

unload()

The unload() method will be invoked by the controller to unload the plugin correctly. It is executed on application shutdown.

Example: unload() function of the forger plugin
public async unload(): Promise<void> {
    await this._forgerPluginDB.close();
}