Basic Tutorial: Hello Exchange Plugin
We will go over how to develop and install a basic plugin called hello-exchange. After developing this plugin, we will be able to call the endpoint GET /plugins/hello-exchange/info and receive the response:
{
public_message: 'Hello Exchange!',
private_message: 'Hello exchange...',
library_message: 'Hello World NPM',
moment_timestamp: <ISO_STRING>
exchange_info: {...}
}
  • public_message: A value set in public_meta.
  • private_message: A value set in meta
  • library_message: The string produced by our third-party npm library.
  • moment_timestamp: Date ISO string produced by a default library (moment).
  • excange_info: The exchange's basic information.

Step 1: Add the basic values of the plugin to your JSON file.

{
name: 'hello-exchange',
version: 1,
bio: 'Demo plugin',
description: 'Demo plugin for proof of concept',
author: 'bitHolla',
...
}

Step 2: Add updatable values in public_meta and meta

The public_meta and meta objects are where you can add values that you may need to change while the plugin is running or values that will be unique for each instance of the plugin.
The public_meta object holds values that will be publicly available. It can be accessed in the script as publicMeta.
The meta object holds values that will only be available to the admin. An example of a good value to put here is an access token for a third-party service like AWS. It can be accessed in the script as meta.
Both the public_meta and meta objects require the keys type, required, description, and value for each parameter added.
  • type is the data type of the parameter (only number, string, boolean, and date-time are allowed).
  • required is whether or not the parameter is required for the plugin.
  • description is the description of the parameter in use.
  • value is the actual value set for the plugin.
{
...
public_meta: {
public_message: {
type: 'string',
required: false,
description: 'Not a secret',
value: 'Hello Exchange!'
}
},
meta: {
public_message: {
type: 'string',
required: false,
description: 'A secret',
value: 'Hello exchange...'
}
},
...
}

Step 3: Add the plugin specific library hello-world-npm to prescript.install

This plugin will use all preconfigured libraries, the default library moment, and a plugin specific library hello-world-npm.
hello-world-npm library only returns the string Hello World NPM. The purpose of this library for this plugin is to demonstrate how to use NPM libraries inside plugins.
To have the plugin add this library on installation, include the library name inside the install array in the prescript object. To specify a version, include the @ symbol with the version desired (similar to how basic npm install works).
{
...
prescript: {
install: ['hello-world-npm']
},
...
}

Step 4: Set script as the JavaScript ES6+ code for the plugin

The code in script is what is run for a plugin. The code must be written in ES6+ and also has to be minified before. The library we recommend is uglify-es.
The code for our plugin is below.
'use strict';
const helloWorld = require('hello-world-npm');
const moment = require('moment');
const { app, toolsLib, loggerPlugin } = this.pluginLibraries;
const { publicMeta, meta } = this.configValues;
const init = async () => {
loggerPlugin.info(
'HELLO-EXCHANGE PLUGIN initializing...'
);
if (!meta.private_message.value) {
throw new Error('Configuration value private required');
}
};
init()
.then(() => {
app.get('/plugins/hello-exchange/info', (req, res) => {
loggerPlugin.verbose(
req.uuid,
'GET /plugins/hello-exchange/info'
);
return res.json({
public_message: publicMeta.public_message.value,
private_message: meta.private_message.value,
library_message: helloWorld(),
moment_timestamp: moment().toISOString(),
exchange_info: toolsLib.getKitConfig().info
});
});
})
.catch((err) => {
loggerPlugin.error(
'HELLO-EXCHANGE PLUGIN error during initialization',
err.message
);
});
Let's break the code down:
const helloWorld = require('hello-world-npm');
const moment = require('moment');
const { app, toolsLib, loggerPlugin } = this.pluginLibraries;
We are first importing all the libraries required for this plugin. hello-world-npm and moment are plugin specific and default libraries, respectively. Both can be imported using require. app, toolsLib, and loggerPlugin are preconfigured libraries that can be accessed in this.pluginLibraries.
const { publicMeta, meta } = this.configValues;
The parameters added to public_meta and meta are stored as publicMeta and meta in this.configValues. We are getting our public_message parameter from publicMeta and private_message parameter from our meta.
const init = async () => {
loggerPlugin.info(
'HELLO-EXCHANGE PLUGIN initializing...'
);
if (!meta.private_message.value) {
throw new Error('Configuration value private required');
}
};
init()
.then(() => { ... }
.catch((err) => {
loggerPlugin.error(
'HELLO-EXCHANGE PLUGIN error during initialization',
err.message
);
});
This init function is first run when the plugin is enabled. For our plugin, we are checking if the value of private_message is configured. If not, the core plugin script won't run until the value is configured.
app.get('/plugins/hello-exchange/info', (req, res) => {
loggerPlugin.verbose(
req.uuid,
'GET /plugins/hello-exchange/info'
);
return res.json({
public_message: publicMeta.public_message.value,
private_message: meta.private_message.value,
library_message: helloWorld(),
moment_timestamp: moment().toISOString(),
exchange_info: toolsLib.getKitConfig().info
});
});
We are creating an endpoint GET /plugins/hello-exchange/info that returns our configuration values, the output from hello-world-npm, the current timestamp with moment, and the exchange info with toolsLib. We are also logging the request uuid with loggerPlugin.
All endpoints for plugins should be formatted as /plugins/<PLUGIN_NAME>/...
Once we have our script for our plugin, we have to wrap it all into a string.
`'use strict';
const helloWorld = require('hello-world-npm');
const moment = require('moment');
const { app, toolsLib, loggerPlugin } = this.pluginLibraries;
const { publicMeta, meta } = this.configValues;
const init = async () => {
loggerPlugin.info(
'HELLO-EXCHANGE PLUGIN initializing...'
);
if (!meta.private_message.value) {
throw new Error('Configuration value private required');
}
};
init()
.then(() => {
app.get('/plugins/hello-exchange/info', (req, res) => {
loggerPlugin.verbose(
req.uuid,
'GET /plugins/hello-exchange/info'
);
return res.json({
public_message: publicMeta.public_message.value,
private_message: meta.private_message.value,
library_message: helloWorld(),
moment_timestamp: moment().toISOString(),
exchange_info: toolsLib.getKitConfig().info
});
});
})
.catch((err) => {
loggerPlugin.error(
'HELLO-EXCHANGE PLUGIN error during initialization',
err.message
);
});
`
We also recommend minifying the script before you install your plugin. Your script will be minified after installation automatically but doing so before will ensure that the minifying process will succeed. We recommend using the library uglify-es.
const UglifyEs = require('uglify-es');
const minifiedScript = UglifyEs.minify(`
'use strict';
const helloWorld = require('hello-world-npm');
const moment = require('moment');
const { app, toolsLib, loggerPlugin } = this.pluginLibraries;
const { publicMeta, meta } = this.configValues;
const init = async () => {
loggerPlugin.info(
'HELLO-EXCHANGE PLUGIN initializing...'
);
if (!meta.private_message.value) {
throw new Error('Configuration value private required');
}
};
init()
.then(() => {
app.get('/plugins/hello-exchange/info', (req, res) => {
loggerPlugin.verbose(
req.uuid,
'GET /plugins/hello-exchange/info'
);
return res.json({
public_message: publicMeta.public_message.value,
private_message: meta.private_message.value,
library_message: helloWorld(),
moment_timestamp: moment().toISOString(),
exchange_info: toolsLib.getKitConfig().info
});
});
})
.catch((err) => {
loggerPlugin.error(
'HELLO-EXCHANGE PLUGIN error during initialization',
err.message
);
});
`).code;
console.log(minifiedScript);
// "script": "\"use strict\";const{publicMeta:publicMeta,meta:meta}=this.configValues,{app:app,loggerPlugin:loggerPlugin,toolsLib:toolsLib}=this.pluginLibraries,helloWorld=require(\"hello-world-npm\"),moment=require(\"moment\"),init=async()=>{if(loggerPlugin.info(\"HELLO-EXCHANGE PLUGIN initializing...\"),!meta.private.value)throw new Error(\"Configuration value private required\")};init().then(()=>{app.get(\"/plugins/hello-exchange/info\",(e,i)=>(loggerPlugin.verbose(e.uuid,\"GET /plugins/hello-exchange/info\"),i.json({public_message:publicMeta.public.value,private_message:meta.private.value,library_message:helloWorld(),moment_timestamp:moment().toISOString(),exchange_info:toolsLib.getKitConfig().info})))}).catch(e=>{loggerPlugin.error(\"HELLO-EXCHANGE PLUGIN error during initialization\",e.message)});",
Once minified, add the script to the script field in your plugin JSON file.
{
...
script: "\"use strict\";const{publicMeta:publicMeta,meta:meta}=this.configValues,{app:app,loggerPlugin:loggerPlugin,toolsLib:toolsLib}=this.pluginLibraries,helloWorld=require(\"hello-world-npm\"),moment=require(\"moment\"),init=async()=>{if(loggerPlugin.info(\"HELLO-EXCHANGE PLUGIN initializing...\"),!meta.private.value)throw new Error(\"Configuration value private required\")};init().then(()=>{app.get(\"/plugins/hello-exchange/info\",(e,i)=>(loggerPlugin.verbose(e.uuid,\"GET /plugins/hello-exchange/info\"),i.json({public_message:publicMeta.public.value,private_message:meta.private.value,library_message:helloWorld(),moment_timestamp:moment().toISOString(),exchange_info:toolsLib.getKitConfig().info})))}).catch(e=>{loggerPlugin.error(\"HELLO-EXCHANGE PLUGIN error during initialization\",e.message)});",
...
}

Step 5: Install the plugin

Once you've followed the steps above, you should have a JSON object for your plugin that looks like the one below:
{
"name": "hello-exchange",
"version": 1,
"type": null,
"author": "bitHolla",
"bio": "Say hello from an exchange",
"description": "Demo plugin for proof of concept",
"documentation": null,
"logo": null,
"icon": null,
"url": null,
"public_meta": {
"public_message": {
"type": "string",
"required": false,
"description": "Not a secret",
"value": "Hello Exchange!"
}
},
"meta": {
"private_message": {
"type": "string",
"required": false,
"description": "A secret",
"value": "hello exchange..."
}
},
"prescript": {
"install": [
"hello-world-npm"
],
"run": null
},
"postscript": {
"run": null
},
"script": "\"use strict\";const{publicMeta:publicMeta,meta:meta}=this.configValues,{app:app,loggerPlugin:loggerPlugin,toolsLib:toolsLib}=this.pluginLibraries,helloWorld=require(\"hello-world-npm\"),moment=require(\"moment\"),init=async()=>{if(loggerPlugin.info(\"HELLO-EXCHANGE PLUGIN initializing...\"),!meta.private.value)throw new Error(\"Configuration value private required\")};init().then(()=>{app.get(\"/plugins/hello-exchange/info\",(e,i)=>(loggerPlugin.verbose(e.uuid,\"GET /plugins/hello-exchange/info\"),i.json({public_message:publicMeta.public.value,private_message:meta.private.value,library_message:helloWorld(),moment_timestamp:moment().toISOString(),exchange_info:toolsLib.getKitConfig().info})))}).catch(e=>{loggerPlugin.error(\"HELLO-EXCHANGE PLUGIN error during initialization\",e.message)});",
"admin_view": null,
"web_view": null
}
To install a plugin, you can use the endpoint POST <API_URL>/plugins with the JSON object above passed as the request body.
Once installed, you will be able to access the endpoint GET /plugins/hello-exchange/info. Your response should be:
{
public_message: "Hello Exchange!",
private_message: "hello exchange...",
library_message: "Hello World NPM",
moment_timestamp: "2021-11-23T03:07:12.066Z",
exchange_info: {
name: "brandonlocalexchange",
active: true,
exchange_id: 1,
user_id: 7,
url: "http://192.168.0.39",
is_trial: false,
created_at: "2021-03-30T06:47:51.800Z",
expiry: "2021-04-30T23:59:59.999Z",
collateral_level: "zero",
type: "DIY",
plan: null,
period: null,
status: true,
initialized: true
}
}
Congratulations, you've created your first plugin!
Copy link
On this page
Step 1: Add the basic values of the plugin to your JSON file.
Step 2: Add updatable values in public_meta and meta
Step 3: Add the plugin specific library hello-world-npm to prescript.install
Step 4: Set script as the JavaScript ES6+ code for the plugin
Step 5: Install the plugin