Skip to content


Developing server-side code requires a lot of testing. Each user transaction should be automated in such a way that enables you to test each transaction with ease. We use the npm library vorpal to create CLI commands that can be used in automated testing scripts.


Below are some helpful packages we recommend using for creating a CLI: fs, path, vorpal, shardus-crypto-utils, axios.

const fs = require('fs')
const { resolve } = require('path')
const path = require('path')
const vorpal = require('vorpal')()
const crypto = require('shardus-crypto-utils')
const stringify = require('fast-stable-stringify')
const axios = require('axios')
// Initialize the crypto module using the same hash-key as you do in the server side code


Setting up the CLI requires loading wallet data; defining the host server and archive-server; and implementing helper functions for creating accounts, getting seed nodes, and injecting transactions.

let USER
let HOST = process.argv[2] || 'localhost:9001'
const ARCHIVESERVER = process.argv[3] || 'localhost:4000'
const network = '0'.repeat(64)
const walletFile = resolve('./wallet.json')
let walletEntries = {}

console.log(`Using ${HOST} as node for queries and transactions.`)

try {
  walletEntries = require(walletFile)
} catch (e) {
  saveEntries(walletEntries, walletFile)
  console.log(`Created wallet file '${walletFile}'.`)

console.log(`Loaded wallet entries from '${walletFile}'.`)

function saveEntries(entries, file) {
  const stringifiedEntries = JSON.stringify(entries, null, 2)
  fs.writeFileSync(file, stringifiedEntries)

function createAccount(keys = crypto.generateKeypair()) {
  return {
    address: keys.publicKey,

function createAccounts(num) {
  const accounts = new Array(num).fill().map(account => createAccount())
  return accounts

async function getSeedNodes() {
  const result = await axios.get(`http://${ARCHIVESERVER}/nodelist`) // await utils.getJson(`${glob.seedNode}/nodelist`)

  let seedNodes = []
  const nodelist =
  if (nodelist !== null) {
    // Filter out all non-active nodes. dont filter yet no one will say active.
    // nodelist = nodelist.filter(node => node.status ? node.status === 'active' : false)
    seedNodes = nodelist
  return seedNodes

async function injectTx(tx) {
  try {
    const res = await`http://${HOST}/inject`, tx)
  } catch (err) {
    return err.message


There are 2 commands that we can have vorpal execute at the start of the program before we start injecting transactions. init is a command that lets you start the CLI using the wallet of your choice, and wallet create <name> can create another named wallet, or return its data if it already exists. It's also nice to have a command to switch hosts (the Shardus node you are sending requests to).

// Command to start the CLI with a named wallet
vorpal.command('init', 'sets the user wallet if it exists, else creates it').action(function(_, callback) {
      type: 'input',
      name: 'user',
      message: 'Enter wallet name: ',
    result => {
      callback(null, vorpal.execSync('wallet create ' + result.user))

vorpal.command('wallet create <name>', 'creates a wallet <name>').action(function(args, callback) {
  if (typeof walletEntries[] !== 'undefined' && walletEntries[] !== null) {
    return walletEntries[]
  } else {
    const user = createEntry(,
    return user

vorpal.command('use host <host>', 'uses <host> as the node for queries and transactions').action(function(args, callback) {
  HOST =
  this.log(`Set ${} as coin-app node for transactions.`)



Here's an example of a transaction command for sending a verification code to validate your email address.

vorpal.command('verify', 'verifies your email address').action(async function(_, callback) {
  const answer = await this.prompt({
    type: 'input',
    name: 'code',
    message: 'Enter the verification code sent to your email address: ',
    validate: result => {
      result = result.split` `.join``
      if (typeof result === 'string' && result.length === 6) {
        return true
      } else {
        return 'You need to provide the 6 digit code'
  const tx = {
    type: 'verify',
    from: USER.address,
    code: answer.code,
  crypto.signObj(tx, USER.keys.secretKey, USER.keys.publicKey)
  injectTx(tx).then(res => {

First, we wait for a prompt to be filled out by the user, then we create a transaction of the type verify and include our wallet address, the code from the prompt, and the current time using Finally, we sign the transaction and use the injectTx helper function to submit the transaction to the network.


End the file by calling the following commands to set up the CLI delimiter, and our USER data with the init command.

vorpal.exec('init').then(res => (USER = res))

By now you should be up and running and easily create more transaction commands for everything the app needs.