CVE-2019-16762 (published 2019-11-15T23:15Z)

A specially crafted Bitcoin script can cause a discrepancy between the specified SLP consensus rules and the validation result of the slpjs npm package. An attacker could create a specially crafted Bitcoin script in order to cause a hard-fork from the SLP consensus. Affected users can upgrade to any version >= 0.21.4.

Severity: MEDIUM
CVSS v2 base score: 4.9 (AV:N/AC:M/Au:S/C:N/I:P/A:P)
CVSS v3 base score: 6.1, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:N/I:H/A:H)

Weakness information:
cwe_id: CWE-20
cwe_name: Improper Input Validation
description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
extended_description: 
	   Input validation is a frequently-used technique
	   for checking potentially dangerous inputs in order to
	   ensure that the inputs are safe for processing within the
	   code, or when communicating with other components.  When
	   software does not validate input properly, an attacker is
	   able to craft the input in a form that is not expected by
	   the rest of the application. This will lead to parts of the
	   system receiving unintended input, which may result in
	   altered control flow, arbitrary control of a resource, or
	   arbitrary code execution.
	   Input validation is not the only technique for
	   processing input, however.  Other techniques attempt to
	   transform potentially-dangerous input into something safe, such
	   as filtering (CWE-790) - which attempts to remove dangerous
	   inputs - or encoding/escaping (CWE-116), which attempts to
	   ensure that the input is not misinterpreted when it is included
	   in output to another component. Other techniques exist as well
	   (see CWE-138 for more examples.)
	   Input validation can be applied to:
	   
	     raw data - strings, numbers, parameters, file contents, etc.
	     metadata - information about the raw data, such as headers or size
	   
	   Data can be simple or structured.  Structured data
	   can be composed of many nested layers, composed of
	   combinations of metadata and raw data, with other simple or
	   structured data.
	   Many properties of raw data or metadata may need
	   to be validated upon entry into the code, such
	   as:
	   
	     specified quantities such as size, length, frequency, price, rate, number of operations, time, etc.
	     implied or derived quantities, such as the actual size of a file instead of a specified size
	     indexes, offsets, or positions into more complex data structures
	     symbolic keys or other elements into hash tables, associative arrays, etc.
	     well-formedness, i.e. syntactic correctness - compliance with expected syntax 
	     lexical token correctness - compliance with rules for what is treated as a token
	     specified or derived type - the actual type of the input (or what the input appears to be)
	     consistency - between individual data elements, between raw data and metadata, between references, etc.
	     conformance to domain-specific rules, e.g. business logic 
	     equivalence - ensuring that equivalent inputs are treated the same
	     authenticity, ownership, or other attestations about the input, e.g. a cryptographic signature to prove the source of the data
	   
	   Implied or derived properties of data must often
	   be calculated or inferred by the code itself.  Errors in
	   deriving properties may be considered a contributing factor
	   to improper input validation.
	   
	   Note that "input validation" has very different
	   meanings to different people, or within different
	   classification schemes.  Caution must be used when
	   referencing this CWE entry or mapping to it.  For example,
	   some weaknesses might involve inadvertently giving control
	   to an attacker over an input when they should not be able
	   to provide an input at all, but sometimes this is referred
	   to as input validation.
	   Finally, it is important to emphasize that the
	   distinctions between input validation and output escaping
	   are often blurred, and developers must be careful to
	   understand the difference, including how input validation
	   is not always sufficient to prevent vulnerabilities,
	   especially when less stringent data types must be
	   supported, such as free-form text. Consider a SQL injection
	   scenario in which a person's last name is inserted into a
	   query. The name "O'Reilly" would likely pass the validation
	   step since it is a common last name in the English
	   language. However, this valid name cannot be directly
	   inserted into the database because it contains the "'"
	   apostrophe character, which would need to be escaped or
	   otherwise transformed. In this case, removing the
	   apostrophe might reduce the risk of SQL injection, but it
	   would produce incorrect behavior because the wrong name
	   would be recorded.
       
         

Fix information:
repo_url: https://github.com/simpleledger/slpjs
commit_details: repo_url: https://github.com/simpleledger/slpjs
author: James Cramer
author_date: 2019-10-26 16:56:21-04:00
msg: 0.21.4 updates:

* add ts linting / allow json comments
repository: repo_url: https://github.com/simpleledger/slpjs
repo_name: simpleledger/slpjs
description: JavaScript library for Simple Ledger Protocol
repo_language: TypeScript
owner: simpleledger
file_changes: filename: settings.json
change_type: ModificationType.ADD
diff: @@ -0,0 +1,5 @@
+{
+    "files.associations": {
+        "*.json": "jsonc"
+    }
+}
\ No newline at end of file

programming_language: CSS
filename: slp.ts
change_type: ModificationType.MODIFY
diff: @@ -1,22 +1,24 @@
-import { SlpAddressUtxoResult, SlpTransactionDetails, SlpTransactionType, SlpUtxoJudgement, SlpBalancesResult, utxo, SlpVersionType, logger, Primatives } from '../index';
-import { SlpTokenType1 } from './slptokentype1';
-import { Utils } from './utils';
+import {
+    logger, Primatives, SlpAddressUtxoResult, SlpBalancesResult,
+    SlpTransactionDetails, SlpTransactionType, SlpUtxoJudgement, SlpVersionType, utxo } from "../index";
+import { SlpTokenType1 } from "./slptokentype1";
+import { Utils } from "./utils";
 
-import { BITBOX } from 'bitbox-sdk';
-import * as bchaddr from 'bchaddrjs-slp';
-import BigNumber from 'bignumber.js';
+import * as bchaddr from "bchaddrjs-slp";
+import BigNumber from "bignumber.js";
+import { BITBOX } from "bitbox-sdk";
 
 export interface SlpPaymentRequest {
-    address: string,
-    amountBch?: number, 
-    amountToken?: number,
-    tokenId?: string,
-    tokenFlags?: string[]
+    address: string;
+    amountBch?: number;
+    amountToken?: number;
+    tokenId?: string;
+    tokenFlags?: string[];
 }
 
 export interface PushDataOperation {
-    opcode: number, 
-    data: Buffer|null
+    opcode: number;
+    data: Buffer|null;
 }
 
 export interface configBuildNFT1GenesisOpReturn {
@@ -30,10 +32,10 @@ export interface configBuildGenesisOpReturn {
     ticker: string|null;
     name: string|null;
     documentUri: string|null;
-    hash: Buffer|null,
+    hash: Buffer|null;
     decimals: number;
     batonVout: number|null; // normally this is null (for fixed supply) or 2+ for flexible
-    initialQuantity: BigNumber
+    initialQuantity: BigNumber;
 }
 
 export interface configBuildMintOpReturn {
@@ -43,24 +45,24 @@ export interface configBuildMintOpReturn {
 }
 
 export interface configBuildSendOpReturn {
-    tokenIdHex: string; 
-    outputQtyArray: BigNumber[]
+    tokenIdHex: string;
+    outputQtyArray: BigNumber[];
 }
 
 export interface configBuildRawNFT1GenesisTx {
-    slpNFT1GenesisOpReturn: Buffer; 
+    slpNFT1GenesisOpReturn: Buffer;
     mintReceiverAddress: string;
     mintReceiverSatoshis?: BigNumber;
-    //batonReceiverAddress: string|null;
-    //batonReceiverSatoshis?: BigNumber;
+    // batonReceiverAddress: string|null;
+    // batonReceiverSatoshis?: BigNumber;
     bchChangeReceiverAddress: string;
     input_utxos: utxo[];
     parentTokenIdHex: string;
-    //allowed_token_burning: string[]|null;
+    // allowed_token_burning: string[]|null;
 }
 
 export interface configBuildRawGenesisTx {
-    slpGenesisOpReturn: Buffer; 
+    slpGenesisOpReturn: Buffer;
     mintReceiverAddress: string;
     mintReceiverSatoshis?: BigNumber;
     batonReceiverAddress: string|null;
@@ -75,7 +77,7 @@ export interface configBuildRawSendTx {
     input_token_utxos: utxo[];
     tokenReceiverAddressArray: string[];
     bchChangeReceiverAddress: string;
-    requiredNonTokenOutputs?: { satoshis: number, receiverAddress: string }[]
+    requiredNonTokenOutputs?: Array<{ satoshis: number, receiverAddress: string }>;
     extraFee?: number;
 }
 
@@ -104,8 +106,9 @@ export interface configBuildRawBurnTx {
 }
 
 export interface SlpValidator {
-    isValidSlpTxid(txid: string, tokenIdFilter?: string|null, tokenTypeFilter?: number|null, logger?: logger): Promise<boolean>;
     getRawTransactions: (txid: string[]) => Promise<string[]>;
+    isValidSlpTxid(txid: string, tokenIdFilter?: string|null,
+                   tokenTypeFilter?: number|null, logger?: logger): Promise<boolean>;
     validateSlpTransactions(txids: string[]): Promise<string[]>;
 }
 
@@ -114,21 +117,15 @@ export interface SlpProxyValidator extends SlpValidator {
 }
 
 export class Slp {
-    BITBOX: BITBOX;
-    constructor(bitbox: BITBOX) {
-        if(!bitbox)
-            throw Error("Must provide BITBOX instance to class constructor.")
-        this.BITBOX = bitbox;
-    }
 
-    get lokadIdHex() { return "534c5000" }
+    get lokadIdHex() { return "534c5000"; }
 
-    static buildGenesisOpReturn(config: configBuildGenesisOpReturn, type = 0x01) {
+    public static buildGenesisOpReturn(config: configBuildGenesisOpReturn, type = 0x01) {
         let hash;
-        try { 
-            hash = config.hash!.toString('hex')
-        } catch (_) { hash = null }
-        
+        try {
+            hash = config.hash!.toString("hex");
+        } catch (_) { hash = null; }
+
         return SlpTokenType1.buildGenesisOpReturn(
             config.ticker,
             config.name,
@@ -136,106 +133,171 @@ export class Slp {
             hash,
             config.decimals,
             config.batonVout,
-            config.initialQuantity, 
-            type
-        )
+            config.initialQuantity,
+            type,
+        );
     }
 
-    static buildMintOpReturn(config: configBuildMintOpReturn, type = 0x01) {
+    public static buildMintOpReturn(config: configBuildMintOpReturn, type = 0x01) {
         return SlpTokenType1.buildMintOpReturn(
             config.tokenIdHex,
             config.batonVout,
-            config.mintQuantity, 
-            type
-        )
+            config.mintQuantity,
+            type,
+        );
     }
 
-    static buildSendOpReturn(config: configBuildSendOpReturn, type = 0x01) {
+    public static buildSendOpReturn(config: configBuildSendOpReturn, type = 0x01) {
         return SlpTokenType1.buildSendOpReturn(
             config.tokenIdHex,
             config.outputQtyArray,
-            type
-        )
+            type,
+        );
+    }
+
+    public static parseChunkToInt(intBytes: Buffer, minByteLen: number, maxByteLen: number, raise_on_Null = false) {
+        // # Parse data as unsigned-big-endian encoded integer.
+        // # For empty data different possibilities may occur:
+        // #      minByteLen <= 0 : return 0
+        // #      raise_on_Null == False and minByteLen > 0: return None
+        // #      raise_on_Null == True and minByteLen > 0:  raise SlpInvalidOutputMessage
+        if (intBytes.length >= minByteLen && intBytes.length <= maxByteLen) {
+            return intBytes.readUIntBE(0, intBytes.length)
+        }
+        if (intBytes.length === 0 && !raise_on_Null) {
+            return null;
+        }
+        throw Error("Field has wrong length");
+    }
+
+    public static preSendSlpJudgementCheck(txo: SlpAddressUtxoResult, tokenId: string) {
+        if (txo.slpUtxoJudgement === undefined ||
+            txo.slpUtxoJudgement === null ||
+            txo.slpUtxoJudgement === SlpUtxoJudgement.UNKNOWN) {
+            throw Error("There at least one input UTXO that does not have a proper SLP judgement");
+        }
+        if (txo.slpUtxoJudgement === SlpUtxoJudgement.UNSUPPORTED_TYPE) {
+            throw Error("There is at least one input UTXO that is an Unsupported SLP type.");
+        }
+        if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
+            throw Error("There is at least one input UTXO that is a baton.  \
+                        You can only spend batons in a MINT transaction.");
+        }
+        if (txo.slpTransactionDetails) {
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
+                if (!txo.slpUtxoJudgementAmount) {
+                    throw Error("There is at least one input token that does not \
+                                have the 'slpUtxoJudgementAmount' property set.");
+                }
+                if (txo.slpTransactionDetails.tokenIdHex !== tokenId) {
+                    throw Error("There is at least one input UTXO that \
+                                is a different SLP token than the one specified.");
+                }
+                return txo.slpTransactionDetails.tokenIdHex === tokenId;
+            }
+        }
+        return false;
+    }
+    public BITBOX: BITBOX;
+    constructor(bitbox: BITBOX) {
+        if (!bitbox) {
+            throw Error("Must provide BITBOX instance to class constructor.")
+        }
+        this.BITBOX = bitbox;
     }
 
-    buildRawNFT1GenesisTx(config: configBuildRawNFT1GenesisTx, type = 0x01) {
-        let config2: configBuildRawGenesisTx = {
+    public buildRawNFT1GenesisTx(config: configBuildRawNFT1GenesisTx, type = 0x01) {
+        const config2: configBuildRawGenesisTx = {
             slpGenesisOpReturn: config.slpNFT1GenesisOpReturn,
             mintReceiverAddress: config.mintReceiverAddress,
             mintReceiverSatoshis: config.mintReceiverSatoshis,
             batonReceiverAddress: null,
             bchChangeReceiverAddress: config.bchChangeReceiverAddress,
             input_utxos: config.input_utxos,
-            allowed_token_burning: [ config.parentTokenIdHex ]
-        }
+            allowed_token_burning: [ config.parentTokenIdHex ],
+        };
 
         return this.buildRawGenesisTx(config2);
     }
 
-    buildRawGenesisTx(config: configBuildRawGenesisTx, type = 0x01) {
+    public buildRawGenesisTx(config: configBuildRawGenesisTx, type = 0x01) {
 
-        if(config.mintReceiverSatoshis === undefined)
+        if (config.mintReceiverSatoshis === undefined) {
             config.mintReceiverSatoshis = new BigNumber(546);
+        }
 
-        if(config.batonReceiverSatoshis === undefined)
-            config.batonReceiverSatoshis = new BigNumber(546); 
+        if (config.batonReceiverSatoshis === undefined) {
+            config.batonReceiverSatoshis = new BigNumber(546);
+        } 
 
         // Make sure we're not spending any token or baton UTXOs
-        config.input_utxos.forEach(txo => {
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP)
-                return
-            if(config.allowed_token_burning && 
-                txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN && 
-                config.allowed_token_burning!.includes(txo.slpTransactionDetails.tokenIdHex)) {
-                    return
+        config.input_utxos.forEach((txo) => {
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP) {
+                return;
             }
-            else if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
-                throw Error("Input UTXOs included a token for another tokenId.")
+            if (config.allowed_token_burning &&
+                txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN &&
+                config.allowed_token_burning!.includes(txo.slpTransactionDetails.tokenIdHex)) {
+                    return;
+            } else if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
+                throw Error("Input UTXOs included a token for another tokenId.");
             }
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON)
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
                 throw Error("Cannot spend a minting baton.")
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG || txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG)
-                throw Error("Cannot currently spend tokens and baton with invalid DAGs.")
-            throw Error("Cannot spend utxo with no SLP judgement.")
-        })
+            }
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG ||
+                txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG) {
+                throw Error("Cannot currently spend tokens and baton with invalid DAGs.");
+            }
+            throw Error("Cannot spend utxo with no SLP judgement.");
+        });
 
         // Check for slp formatted addresses
-        if (!bchaddr.isSlpAddress(config.mintReceiverAddress))
+        if (!bchaddr.isSlpAddress(config.mintReceiverAddress)) {
             throw new Error("Not an SLP address.");
-        if (config.batonReceiverAddress && !bchaddr.isSlpAddress(config.batonReceiverAddress))
+        }
+        if (config.batonReceiverAddress && !bchaddr.isSlpAddress(config.batonReceiverAddress)) {
             throw new Error("Not an SLP address.");
+        }
 
         config.mintReceiverAddress = bchaddr.toCashAddress(config.mintReceiverAddress);
 
-        let transactionBuilder = new this.BITBOX.TransactionBuilder(Utils.txnBuilderString(config.mintReceiverAddress));
+        const transactionBuilder = new this.BITBOX.TransactionBuilder(
+            Utils.txnBuilderString(config.mintReceiverAddress));
         let satoshis = new BigNumber(0);
         config.input_utxos.forEach(token_utxo => {
             transactionBuilder.addInput(token_utxo.txid, token_utxo.vout);
             satoshis = satoshis.plus(token_utxo.satoshis);
         });
 
-        let genesisCost = this.calculateGenesisCost(config.slpGenesisOpReturn.length, config.input_utxos.length, config.batonReceiverAddress, config.bchChangeReceiverAddress);
-        let bchChangeAfterFeeSatoshis: BigNumber = satoshis.minus(genesisCost);
+        const genesisCost = this.calculateGenesisCost(
+            config.slpGenesisOpReturn.length,
+            config.input_utxos.length,
+            config.batonReceiverAddress,
+            config.bchChangeReceiverAddress);
+        const bchChangeAfterFeeSatoshis: BigNumber = satoshis.minus(genesisCost);
 
         // Genesis OpReturn
         transactionBuilder.addOutput(config.slpGenesisOpReturn, 0);
 
         // Genesis token mint
         transactionBuilder.addOutput(config.mintReceiverAddress, config.mintReceiverSatoshis.toNumber());
-        //bchChangeAfterFeeSatoshis -= config.mintReceiverSatoshis;
+        // bchChangeAfterFeeSatoshis -= config.mintReceiverSatoshis;
 
         // Baton address (optional)
-        let batonvout = this.parseSlpOutputScript(config.slpGenesisOpReturn).batonVout
+        const batonvout = this.parseSlpOutputScript(config.slpGenesisOpReturn).batonVout;
         if (config.batonReceiverAddress) {
             config.batonReceiverAddress = bchaddr.toCashAddress(config.batonReceiverAddress);
-            if(batonvout !== 2)
-                throw Error("batonVout in transaction does not match OP_RETURN data.")
+            if (batonvout !== 2) {
+                throw Error("batonVout in transaction does not match OP_RETURN data.");
+            }
             transactionBuilder.addOutput(config.batonReceiverAddress, config.batonReceiverSatoshis.toNumber());
-            //bchChangeAfterFeeSatoshis -= config.batonReceiverSatoshis;
+            // bchChangeAfterFeeSatoshis -= config.batonReceiverSatoshis;
         } else {
             // Make sure that batonVout is set to null
-            if(batonvout)
-                throw Error("OP_RETURN has batonVout set to vout=" + batonvout + ", but a baton receiver address was not provided.")
+            if (batonvout) {
+                throw Error("OP_RETURN has batonVout set to vout=" + batonvout + ", but a baton receiver address was not provided.");
+            }
         }
 
         // Change (optional)
@@ -247,104 +309,116 @@ export class Slp {
         // sign inputs
         let i = 0;
         for (const txo of config.input_utxos) {
-            let paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
-            transactionBuilder.sign(i, paymentKeyPair, undefined, transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
+            const paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
+            transactionBuilder.sign(i, paymentKeyPair, undefined,
+                transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
             i++;
         }
 
-        let tx = transactionBuilder.build().toHex();
+        const tx = transactionBuilder.build().toHex();
 
         // Check For Low Fee
-        let outValue: number = transactionBuilder.transaction.tx.outs.reduce((v: number, o: any)=>v += o.value, 0);
-        let inValue: BigNumber = config.input_utxos.reduce((v,i)=>v=v.plus(i.satoshis), new BigNumber(0))
-        if(inValue.minus(outValue).isLessThanOrEqualTo(tx.length/2))
-            throw Error("Transaction input BCH amount is too low.  Add more BCH inputs to fund this transaction.")
+        const outValue: number = transactionBuilder.transaction.tx.outs.reduce((v: number, o: any) => v += o.value, 0);
+        const inValue: BigNumber = config.input_utxos.reduce((v, i) => v = v.plus(i.satoshis), new BigNumber(0));
+        if (inValue.minus(outValue).isLessThanOrEqualTo(tx.length / 2)) {
+            throw Error("Transaction input BCH amount is too low.  Add more BCH inputs to fund this transaction.");
+        }
 
         // TODO: Check for fee too large or send leftover to target address
 
         return tx;
     }
 
-    buildRawSendTx(config: configBuildRawSendTx, type = 0x01) {
+    public buildRawSendTx(config: configBuildRawSendTx, type = 0x01) {
 
         // Check proper address formats are given
 
-        config.tokenReceiverAddressArray.forEach(outputAddress => {
-            if (!bchaddr.isSlpAddress(outputAddress))
+        config.tokenReceiverAddressArray.forEach((outputAddress) => {
+            if (!bchaddr.isSlpAddress(outputAddress)) {
                 throw new Error("Token receiver address not in SlpAddr format.");
+            }
         });
 
-        if (!bchaddr.isSlpAddress(config.bchChangeReceiverAddress))
+        if (!bchaddr.isSlpAddress(config.bchChangeReceiverAddress)) {
             throw new Error("Token/BCH change receiver address is not in SLP format.");
+        }
 
         // Parse the SLP SEND OP_RETURN message
 
         const sendMsg = this.parseSlpOutputScript(config.slpSendOpReturn);
-        
+
         // Make sure we're not spending inputs from any other token or baton
 
         let tokenInputQty = new BigNumber(0);
-        config.input_token_utxos.forEach(txo => {
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP)
-                return
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
-                if(txo.slpTransactionDetails.tokenIdHex !== sendMsg.tokenIdHex)
-                    throw Error("Input UTXOs included a token for another tokenId.")
+        config.input_token_utxos.forEach((txo) => {
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP) {
+                return;
+            }
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
+                if (txo.slpTransactionDetails.tokenIdHex !== sendMsg.tokenIdHex) {
+                    throw Error("Input UTXOs included a token for another tokenId.");
+                }
                 tokenInputQty = tokenInputQty.plus(txo.slpUtxoJudgementAmount);
-                return
+                return;
             }
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON)
-                throw Error("Cannot spend a minting baton.")
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG || 
-                txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG)
-                throw Error("Cannot currently spend UTXOs with invalid DAGs.")
-            throw Error("Cannot spend utxo with no SLP judgement.")
-        })
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
+                throw Error("Cannot spend a minting baton.");
+            }
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG ||
+                txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG) {
+                    throw Error("Cannot currently spend UTXOs with invalid DAGs.");
+                }
+            throw Error("Cannot spend utxo with no SLP judgement.");
+        });
 
-        // Make sure the number of output receivers 
+        // Make sure the number of output receivers
         // matches the outputs in the OP_RETURN message.
 
-        let chgAddr = config.bchChangeReceiverAddress ? 1 : 0;
-        if(!sendMsg.sendOutputs)
+        const chgAddr = config.bchChangeReceiverAddress ? 1 : 0;
+        if (!sendMsg.sendOutputs) {
             throw Error("OP_RETURN contains no SLP send outputs.");
-        if(config.tokenReceiverAddressArray.length + chgAddr !== sendMsg.sendOutputs.length)
-            throw Error("Number of token receivers in config does not match the OP_RETURN outputs")
+        }
+        if (config.tokenReceiverAddressArray.length + chgAddr !== sendMsg.sendOutputs.length) {
+            throw Error("Number of token receivers in config does not match the OP_RETURN outputs");
+        }
 
         // Make sure token inputs == token outputs
 
-        let outputTokenQty = sendMsg.sendOutputs.reduce((v,o)=>v=v.plus(o), new BigNumber(0));
-        if(!tokenInputQty.isEqualTo(outputTokenQty))
-            throw Error("Token input quantity does not match token outputs.")
+        const outputTokenQty = sendMsg.sendOutputs.reduce((v, o) => v = v.plus(o), new BigNumber(0));
+        if (!tokenInputQty.isEqualTo(outputTokenQty)) {
+            throw Error("Token input quantity does not match token outputs.");
+        }
 
         // Create a transaction builder
 
-        let transactionBuilder = new this.BITBOX.TransactionBuilder(
+        const transactionBuilder = new this.BITBOX.TransactionBuilder(
             Utils.txnBuilderString(config.tokenReceiverAddressArray[0]));
     //  let sequence = 0xffffffff - 1;
     //  let locktime = 0;
 
         // Calculate the total input amount & add all inputs to the transaction
-        
-        let inputSatoshis = config.input_token_utxos.reduce((t, i) => t.plus(i.satoshis), new BigNumber(0));
+
+        const inputSatoshis = config.input_token_utxos.reduce((t, i) => t.plus(i.satoshis), new BigNumber(0));
         config.input_token_utxos.forEach(
-            token_utxo => transactionBuilder.addInput(token_utxo.txid, token_utxo.vout)); //, sequence);
+            token_utxo => transactionBuilder.addInput(token_utxo.txid, token_utxo.vout)); // , sequence);
 
         // Calculate the amount of outputs set aside for special BCH-only outputs for fee calculation
 
-        let bchOnlyCount = config.requiredNonTokenOutputs ? config.requiredNonTokenOutputs.length : 0;
-        let bcOnlyOutputSatoshis = config.requiredNonTokenOutputs ? config.requiredNonTokenOutputs.reduce((t, v)=> t+=v.satoshis, 0): 0
+        const bchOnlyCount = config.requiredNonTokenOutputs ? config.requiredNonTokenOutputs.length : 0;
+        const bcOnlyOutputSatoshis = config.requiredNonTokenOutputs ?
+                                        config.requiredNonTokenOutputs.reduce((t, v) => t += v.satoshis, 0) : 0;
 
         // Calculate mining fee cost
 
-        let sendCost = this.calculateSendCost(
-                            config.slpSendOpReturn.length, 
-                            config.input_token_utxos.length, 
-                            config.tokenReceiverAddressArray.length + bchOnlyCount, 
+        const sendCost = this.calculateSendCost(
+                            config.slpSendOpReturn.length,
+                            config.input_token_utxos.length,
+                            config.tokenReceiverAddressArray.length + bchOnlyCount,
                             config.bchChangeReceiverAddress);
 
         // Compute BCH change amount
 
-        let bchChangeAfterFeeSatoshis = 
+        const bchChangeAfterFeeSatoshis =
             inputSatoshis
                 .minus(sendCost)
                 .minus(bcOnlyOutputSatoshis)
@@ -360,15 +434,15 @@ export class Slp {
         config.tokenReceiverAddressArray.forEach((outputAddress) => {
             outputAddress = bchaddr.toCashAddress(outputAddress);
             transactionBuilder.addOutput(outputAddress, 546);
-        })
+        });
 
         // Add BCH-only outputs
 
-        if(config.requiredNonTokenOutputs && config.requiredNonTokenOutputs.length > 0) {
+        if (config.requiredNonTokenOutputs && config.requiredNonTokenOutputs.length > 0) {
             config.requiredNonTokenOutputs.forEach((output) => {
-                let outputAddress = bchaddr.toCashAddress(output.receiverAddress);
+                const outputAddress = bchaddr.toCashAddress(output.receiverAddress);
                 transactionBuilder.addOutput(outputAddress, output.satoshis);
-            })
+            });
         }
 
         // Add change, if any
@@ -378,18 +452,20 @@ export class Slp {
             transactionBuilder.addOutput(config.bchChangeReceiverAddress, bchChangeAfterFeeSatoshis.toNumber());
         }
 
-        // Sign txn and add sig to p2pkh input for convenience if wif is provided, 
+        // Sign txn and add sig to p2pkh input for convenience if wif is provided,
         // otherwise skip signing.
 
         let i = 0;
         let isComplete = true;
         for (const txo of config.input_token_utxos) {
-            if(txo.wif) {
-                let paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
-                transactionBuilder.sign(i, paymentKeyPair, undefined, transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
-            } 
-            else
+            if (txo.wif) {
+                const paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
+                transactionBuilder.sign(i, paymentKeyPair, undefined,
+                    transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
+            }
+            else {
                 isComplete = false;
+            }
             i++;
         }
 
@@ -397,34 +473,37 @@ export class Slp {
         // warn user if the transaction was not fully signed
 
         let hex: string;
-        if(!isComplete) {
+        if (!isComplete) {
             console.log("WARNING: Transaction signing is not complete.");
-            let tx = transactionBuilder.transaction.buildIncomplete();
+            const tx = transactionBuilder.transaction.buildIncomplete();
         //  tx.locktime = locktime;
             hex = tx.toHex();
-        } 
-        else
+        } else {
             hex = transactionBuilder.build().toHex();
+        }
 
         // Check For Low Fee
 
-        let outValue: number = transactionBuilder.transaction.tx.outs.reduce((v: number,o: any)=>v+=o.value, 0);
-        let inValue: BigNumber = config.input_token_utxos.reduce((v,i)=>v=v.plus(i.satoshis), new BigNumber(0))
-        if(inValue.minus(outValue).isLessThanOrEqualTo(hex.length/2))
+        const outValue: number = transactionBuilder.transaction.tx.outs.reduce((v: number, o: any) => v += o.value, 0);
+        const inValue: BigNumber = config.input_token_utxos.reduce((v, i) => v = v.plus(i.satoshis), new BigNumber(0));
+        if (inValue.minus(outValue).isLessThanOrEqualTo(hex.length / 2)) {
             throw Error("Transaction input BCH amount is too low.  Add more BCH inputs to fund this transaction.");
+        }
 
         return hex;
     }
 
-    buildRawMintTx(config: configBuildRawMintTx, type = 0x01) {
+    public buildRawMintTx(config: configBuildRawMintTx, type = 0x01) {
 
-        let mintMsg = this.parseSlpOutputScript(config.slpMintOpReturn);
+        const mintMsg = this.parseSlpOutputScript(config.slpMintOpReturn);
 
-        if(config.mintReceiverSatoshis === undefined)
+        if (config.mintReceiverSatoshis === undefined) {
             config.mintReceiverSatoshis = new BigNumber(546);
+        }
 
-        if(config.batonReceiverSatoshis === undefined)
-            config.batonReceiverSatoshis = new BigNumber(546); 
+        if (config.batonReceiverSatoshis === undefined) {
+            config.batonReceiverSatoshis = new BigNumber(546);
+        }
 
         // Check for slp formatted addresses
         if (!bchaddr.isSlpAddress(config.mintReceiverAddress)) {
@@ -434,53 +513,63 @@ export class Slp {
             throw new Error("Baton receiver address not in SLP format.");
         }
         config.mintReceiverAddress = bchaddr.toCashAddress(config.mintReceiverAddress);
-        if(config.batonReceiverAddress)
+        if (config.batonReceiverAddress) {
             config.batonReceiverAddress = bchaddr.toCashAddress(config.batonReceiverAddress);
+        }
 
         // Make sure inputs don't include spending any tokens or batons for other tokenIds
-        config.input_baton_utxos.forEach(txo => {
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP)
-                return
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN)
-                throw Error("Input UTXOs should not include any tokens.")
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
-                if(txo.slpTransactionDetails.tokenIdHex !== mintMsg.tokenIdHex)
-                    throw Error("Cannot spend a minting baton.")
-                return
-            }
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG || txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG)
-                throw Error("Cannot currently spend UTXOs with invalid DAGs.")
-            throw Error("Cannot spend utxo with no SLP judgement.")
-        })
+        config.input_baton_utxos.forEach((txo) => {
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP) {
+                return;
+            }
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
+                throw Error("Input UTXOs should not include any tokens.");
+            }
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
+                if (txo.slpTransactionDetails.tokenIdHex !== mintMsg.tokenIdHex) {
+                    throw Error("Cannot spend a minting baton.");
+                }
+                return;
+            }
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG ||
+                txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG) {
+                throw Error("Cannot currently spend UTXOs with invalid DAGs.");
+            }
+            throw Error("Cannot spend utxo with no SLP judgement.");
+        });
 
         // Make sure inputs include the baton for this tokenId
-        if(!config.input_baton_utxos.find(o => o.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON))
-            Error("There is no baton included with the input UTXOs.")
+        if (!config.input_baton_utxos.find((o) => o.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON)) {
+            Error("There is no baton included with the input UTXOs.");
+        }
 
-        let transactionBuilder = new this.BITBOX.TransactionBuilder(Utils.txnBuilderString(config.mintReceiverAddress));
+        const transactionBuilder = new this.BITBOX.TransactionBuilder(
+            Utils.txnBuilderString(config.mintReceiverAddress));
         let satoshis = new BigNumber(0);
         config.input_baton_utxos.forEach(baton_utxo => {
             transactionBuilder.addInput(baton_utxo.txid, baton_utxo.vout);
             satoshis = satoshis.plus(baton_utxo.satoshis);
         });
 
-        let mintCost = this.calculateGenesisCost(config.slpMintOpReturn.length, config.input_baton_utxos.length, config.batonReceiverAddress, config.bchChangeReceiverAddress);
-        let bchChangeAfterFeeSatoshis = satoshis.minus(mintCost);
+        const mintCost = this.calculateGenesisCost(config.slpMintOpReturn.length, config.input_baton_utxos.length,
+            config.batonReceiverAddress, config.bchChangeReceiverAddress);
+        const bchChangeAfterFeeSatoshis = satoshis.minus(mintCost);
 
         // Mint OpReturn
         transactionBuilder.addOutput(config.slpMintOpReturn, 0);
 
         // Mint token mint
         transactionBuilder.addOutput(config.mintReceiverAddress, config.mintReceiverSatoshis.toNumber());
-        //bchChangeAfterFeeSatoshis -= config.mintReceiverSatoshis;
+        // bchChangeAfterFeeSatoshis -= config.mintReceiverSatoshis;
 
         // Baton address (optional)
         if (config.batonReceiverAddress !== null) {
             config.batonReceiverAddress = bchaddr.toCashAddress(config.batonReceiverAddress);
-            if(this.parseSlpOutputScript(config.slpMintOpReturn).batonVout !== 2)
-                throw Error("batonVout in transaction does not match OP_RETURN data.")
+            if (this.parseSlpOutputScript(config.slpMintOpReturn).batonVout !== 2) {
+                throw Error("batonVout in transaction does not match OP_RETURN data.");
+            }
             transactionBuilder.addOutput(config.batonReceiverAddress, config.batonReceiverSatoshis.toNumber());
-            //bchChangeAfterFeeSatoshis -= config.batonReceiverSatoshis;
+            // bchChangeAfterFeeSatoshis -= config.batonReceiverSatoshis;
         }
 
         // Change (optional)
@@ -492,92 +581,107 @@ export class Slp {
         // sign inputs
         let i = 0;
         for (const txo of config.input_baton_utxos) {
-            let paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
-            transactionBuilder.sign(i, paymentKeyPair, undefined, transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
+            const paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
+            transactionBuilder.sign(i, paymentKeyPair, undefined,
+                transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
             i++;
         }
 
-        let tx = transactionBuilder.build().toHex();
+        const tx = transactionBuilder.build().toHex();
 
         // Check For Low Fee
-        let outValue: number = transactionBuilder.transaction.tx.outs.reduce((v: number,o: any)  => v += o.value, 0);
-        let inValue: BigNumber = config.input_baton_utxos.reduce((v, i)=> v = v.plus(i.satoshis), new BigNumber(0))
-        if(inValue.minus(outValue).isLessThanOrEqualTo(tx.length / 2))
-            throw Error("Transaction input BCH amount is too low.  Add more BCH inputs to fund this transaction.")
+        const outValue: number = transactionBuilder.transaction.tx.outs.reduce((v: number, o: any)  => v += o.value, 0);
+        const inValue: BigNumber = config.input_baton_utxos.reduce((v, i) => v = v.plus(i.satoshis), new BigNumber(0));
+        if (inValue.minus(outValue).isLessThanOrEqualTo(tx.length / 2)) {
+            throw Error("Transaction input BCH amount is too low.  Add more BCH inputs to fund this transaction.");
+        }
 
         // TODO: Check for fee too large or send leftover to target address
 
         return tx;
     }
 
-    buildRawBurnTx(burnAmount: BigNumber, config: configBuildRawBurnTx, type = 0x01) {
+    public buildRawBurnTx(burnAmount: BigNumber, config: configBuildRawBurnTx, type = 0x01) {
         let sendMsg: SlpTransactionDetails;
 
-        if(config.slpBurnOpReturn) {
+        if (config.slpBurnOpReturn) {
             sendMsg = this.parseSlpOutputScript(config.slpBurnOpReturn);
-            if(!sendMsg.sendOutputs)
+            if (!sendMsg.sendOutputs) {
                 throw Error("OP_RETURN contains no SLP send outputs for token change.");
-    
-            if(sendMsg.sendOutputs!.length !== 2)
+            }
+
+            if (sendMsg.sendOutputs!.length !== 2) {
                 throw Error("Burn transaction must have only a single change receiver for token change.");
+            }
 
-            if(sendMsg.sendOutputs!.length === 2 && !config.bchChangeReceiverAddress)
+            if (sendMsg.sendOutputs!.length === 2 && !config.bchChangeReceiverAddress) {
                 throw new Error("Token/BCH change address is not provided.");
+            }
 
-            if (!bchaddr.isSlpAddress(config.bchChangeReceiverAddress))
+            if (!bchaddr.isSlpAddress(config.bchChangeReceiverAddress)) {
                 throw new Error("Token/BCH change receiver address is not in SLP format.");
-        } else if(!config.tokenIdHex)
+            }
+        } else if (!config.tokenIdHex) {
             console.log("[WARNING!] Include 'config.tokenIdHex' in order to accidental token burning.  To supress this log message set 'config.tokenIdHex' to an empty string.")
+ }
 
         // Make sure not spending any other tokens or baton UTXOs
         let tokenInputQty = new BigNumber(0);
-        config.input_token_utxos.forEach(txo => {
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP)
-                return
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
-                if(sendMsg) {
-                    if(txo.slpTransactionDetails.tokenIdHex !== sendMsg.tokenIdHex)
-                        throw Error("Input UTXOs included a token for another tokenId.")
+        config.input_token_utxos.forEach((txo) => {
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP) {
+                return;
+            }
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
+                if (sendMsg) {
+                    if (txo.slpTransactionDetails.tokenIdHex !== sendMsg.tokenIdHex) {
+                        throw Error("Input UTXOs included a token for another tokenId.");
+                    }
                 } else {
-                    if(txo.slpTransactionDetails.tokenIdHex !== config.tokenIdHex)
-                        throw Error("Input UTXOs included a token for another tokenId.")
+                    if (txo.slpTransactionDetails.tokenIdHex !== config.tokenIdHex) {
+                        throw Error("Input UTXOs included a token for another tokenId.");
+                    }
                 }
                 tokenInputQty = tokenInputQty.plus(txo.slpUtxoJudgementAmount);
-                return
+                return;
             }
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON)
-                throw Error("Cannot spend a minting baton.")
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG || txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG)
-                throw Error("Cannot currently spend UTXOs with invalid DAGs.")
-            throw Error("Cannot spend utxo with no SLP judgement.")
-        })
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
+                throw Error("Cannot spend a minting baton.");
+            }
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG || txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG) {
+                throw Error("Cannot currently spend UTXOs with invalid DAGs.");
+            }
+            throw Error("Cannot spend utxo with no SLP judgement.");
+        });
 
         // Make sure the number of output receivers matches the outputs in the OP_RETURN message.
-        if(config.slpBurnOpReturn) {
-            //let chgAddr = config.bchChangeReceiverAddress ? 1 : 0;
+        if (config.slpBurnOpReturn) {
+            // let chgAddr = config.bchChangeReceiverAddress ? 1 : 0;
             // Make sure token inputs equals token outputs in OP_RETURN
-            let outputTokenQty = sendMsg!.sendOutputs!.reduce((v,o) => v=v.plus(o), new BigNumber(0));
-            
-            if(!tokenInputQty.minus(outputTokenQty).isEqualTo(burnAmount))
-                throw Error("Token burn output quantity must be less than token input quantity.")
+            const outputTokenQty = sendMsg!.sendOutputs!.reduce((v, o) => v = v.plus(o), new BigNumber(0));
+
+            if (!tokenInputQty.minus(outputTokenQty).isEqualTo(burnAmount)) {
+                throw Error("Token burn output quantity must be less than token input quantity.");
+            }
         }
 
-        let transactionBuilder = new this.BITBOX.TransactionBuilder(Utils.txnBuilderString(config.bchChangeReceiverAddress));
+        const transactionBuilder = new this.BITBOX.TransactionBuilder(
+            Utils.txnBuilderString(config.bchChangeReceiverAddress));
         let inputSatoshis = new BigNumber(0);
         config.input_token_utxos.forEach(token_utxo => {
             transactionBuilder.addInput(token_utxo.txid, token_utxo.vout);
             inputSatoshis = inputSatoshis.plus(token_utxo.satoshis);
         });
 
-        let msgLength = config.slpBurnOpReturn ? config.slpBurnOpReturn.length : 0;
-        let sendCost = this.calculateSendCost(msgLength, config.input_token_utxos.length, msgLength > 0 ? 1 : 0, config.bchChangeReceiverAddress);
-        let bchChangeAfterFeeSatoshis = inputSatoshis.minus(sendCost);
+        const msgLength = config.slpBurnOpReturn ? config.slpBurnOpReturn.length : 0;
+        const sendCost = this.calculateSendCost(msgLength,
+            config.input_token_utxos.length, msgLength > 0 ? 1 : 0, config.bchChangeReceiverAddress);
+        const bchChangeAfterFeeSatoshis = inputSatoshis.minus(sendCost);
 
         // Burn change OpReturn / token change output
-        if(config.slpBurnOpReturn) {
+        if (config.slpBurnOpReturn) {
             transactionBuilder.addOutput(config.slpBurnOpReturn!, 0);
 
-            let outputAddress = bchaddr.toCashAddress(config.bchChangeReceiverAddress);
+            const outputAddress = bchaddr.toCashAddress(config.bchChangeReceiverAddress);
             transactionBuilder.addOutput(outputAddress, 546);
         }
 
@@ -590,57 +694,67 @@ export class Slp {
         // sign inputs
         let i = 0;
         for (const txo of config.input_token_utxos) {
-            let paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
-            transactionBuilder.sign(i, paymentKeyPair, undefined, transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
+            const paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
+            transactionBuilder.sign(i, paymentKeyPair, undefined,
+                transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
             i++;
         }
 
-        let tx = transactionBuilder.build().toHex();
+        const tx = transactionBuilder.build().toHex();
 
         // Check For Low Fee
-        let outValue: number = transactionBuilder.transaction.tx.outs.reduce((v: number,o: any) => v+=o.value, 0);
-        let inValue: BigNumber = config.input_token_utxos.reduce((v,i) => v=v.plus(i.satoshis), new BigNumber(0))
-        if(inValue.minus(outValue).isLessThanOrEqualTo(tx.length/2))
-            throw Error("Transaction input BCH amount is too low.  Add more BCH inputs to fund this transaction.")
+        const outValue: number = transactionBuilder.transaction.tx.outs.reduce((v: number, o: any) => v += o.value, 0);
+        const inValue: BigNumber = config.input_token_utxos.reduce((v, i) => v = v.plus(i.satoshis), new BigNumber(0));
+        if (inValue.minus(outValue).isLessThanOrEqualTo(tx.length / 2)) {
+            throw Error("Transaction input BCH amount is too low.  Add more BCH inputs to fund this transaction.");
+        }
 
         return tx;
     }
 
-    buildRawBchOnlyTx(config: configBuildBchSendTx) {
-        config.bchReceiverAddressArray.forEach(outputAddress => {
-            if (!bchaddr.isSlpAddress(outputAddress) && !bchaddr.isCashAddress(outputAddress))
+    public buildRawBchOnlyTx(config: configBuildBchSendTx) {
+        config.bchReceiverAddressArray.forEach((outputAddress) => {
+            if (!bchaddr.isSlpAddress(outputAddress) && !bchaddr.isCashAddress(outputAddress)) {
                 throw new Error("Token receiver address not in SlpAddr or CashAddr format.");
+            }
         });
 
         // Make sure not spending ANY tokens or baton UTXOs
-        config.input_token_utxos.forEach(txo => {
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP)
-                return
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
+        config.input_token_utxos.forEach((txo) => {
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP) {
+                return;
+            }
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
                 throw Error("Input UTXOs included a token for another tokenId.");
             }
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON)
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
                 throw Error("Cannot spend a minting baton.");
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG || txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG)
+            }
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG ||
+                txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG) {
                 throw Error("Cannot currently spend UTXOs with invalid DAGs.");
+            }
             throw Error("Cannot spend utxo with no SLP judgement.");
-        })
+        });
 
-        let transactionBuilder = new this.BITBOX.TransactionBuilder(Utils.txnBuilderString(config.bchReceiverAddressArray[0]));
+        const transactionBuilder = new this.BITBOX.TransactionBuilder(
+            Utils.txnBuilderString(config.bchReceiverAddressArray[0]));
         let inputSatoshis = new BigNumber(0);
         config.input_token_utxos.forEach(token_utxo => {
             transactionBuilder.addInput(token_utxo.txid, token_utxo.vout);
             inputSatoshis = inputSatoshis.plus(token_utxo.satoshis);
         });
 
-        let sendCost = this.calculateSendCost(0, config.input_token_utxos.length, config.bchReceiverAddressArray.length, config.bchChangeReceiverAddress, 1, false);
-        let bchChangeAfterFeeSatoshis = inputSatoshis.minus(sendCost).minus(config.bchReceiverSatoshiAmounts.reduce((t, v) => t = t.plus(v), new BigNumber(0)));
+        const sendCost = this.calculateSendCost(0, config.input_token_utxos.length,
+            config.bchReceiverAddressArray.length, config.bchChangeReceiverAddress, 1, false);
+        const bchChangeAfterFeeSatoshis = inputSatoshis.minus(sendCost)
+            .minus(config.bchReceiverSatoshiAmounts.reduce((t, v) => t = t.plus(v), new BigNumber(0)));
 
         // BCH outputs
         config.bchReceiverAddressArray.forEach((outputAddress, i) => {
             outputAddress = bchaddr.toCashAddress(outputAddress);
             transactionBuilder.addOutput(outputAddress, Math.round(config.bchReceiverSatoshiAmounts[i].toNumber()));
-        })
+        });
 
         // Change
         if (bchChangeAfterFeeSatoshis.isGreaterThan(new BigNumber(546))) {
@@ -651,100 +765,121 @@ export class Slp {
         // sign inputs
         let i = 0;
         for (const txo of config.input_token_utxos) {
-            let paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
-            transactionBuilder.sign(i, paymentKeyPair, undefined, transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
+            const paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
+            transactionBuilder.sign(i, paymentKeyPair, undefined,
+                transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
             i++;
         }
 
-        let tx = transactionBuilder.build().toHex();
+        const tx = transactionBuilder.build().toHex();
 
         // Check For Low Fee
-        let outValue: number = transactionBuilder.transaction.tx.outs.reduce((v: number,o: any)=>v+=o.value, 0);
-        let inValue: BigNumber = config.input_token_utxos.reduce((v,i)=>v=v.plus(i.satoshis), new BigNumber(0))
-        if(inValue.minus(outValue).isLessThanOrEqualTo(tx.length/2))
-            throw Error("Transaction input BCH amount is too low.  Add more BCH inputs to fund this transaction.")
+        const outValue: number = transactionBuilder.transaction.tx.outs.reduce((v: number, o: any) => v += o.value, 0);
+        const inValue: BigNumber = config.input_token_utxos.reduce((v, i) => v = v.plus(i.satoshis), new BigNumber(0));
+        if (inValue.minus(outValue).isLessThanOrEqualTo(tx.length / 2)) {
+            throw Error("Transaction input BCH amount is too low.  Add more BCH inputs to fund this transaction.");
+        }
 
         // TODO: Check for fee too large or send leftover to target address
 
         return tx;
     }
 
-    parseSlpOutputScript(outputScript: Buffer): SlpTransactionDetails {
-        let slpMsg = <SlpTransactionDetails>{};
-        let chunks: (Buffer|null)[];
+    public parseSlpOutputScript(outputScript: Buffer): SlpTransactionDetails {
+        const slpMsg =  {} as SlpTransactionDetails;
+        let chunks: Array<Buffer|null>;
         try {
             chunks = this.parseOpReturnToChunks(outputScript);
-        } catch(e) {
-            throw Error('Bad OP_RETURN');
-        }
-        if(chunks.length === 0)
-            throw Error('Empty OP_RETURN');
-        if(!chunks[0])
-            throw Error("Not SLP")
-        if(!chunks[0]!.equals(Buffer.from(this.lokadIdHex, 'hex')))
-            throw Error('Not SLP');
-        if(chunks.length === 1)
+        } catch (e) {
+            throw Error("Bad OP_RETURN");
+        }
+        if (chunks.length === 0) {
+            throw Error("Empty OP_RETURN");
+        }
+        if (!chunks[0]) {
+            throw Error("Not SLP");
+        }
+        if (!chunks[0]!.equals(Buffer.from(this.lokadIdHex, "hex"))) {
+            throw Error("Not SLP");
+        }
+        if (chunks.length === 1) {
             throw Error("Missing token versionType");
+        }
         // # check if the token version is supported
-        if(!chunks[1])
-            throw Error("Bad versionType buffer")
-        slpMsg.versionType = <SlpVersionType>Slp.parseChunkToInt(chunks[1]!, 1, 2, true);
-        let supportedTypes = [   
-                SlpVersionType.TokenVersionType1, 
+        if (!chunks[1]) {
+            throw Error("Bad versionType buffer");
+        }
+        slpMsg.versionType = ( Slp.parseChunkToInt(chunks[1]!, 1, 2, true) as SlpVersionType);
+        const supportedTypes = [
+                SlpVersionType.TokenVersionType1,
                 SlpVersionType.TokenVersionType1_NFT_Parent,
                 SlpVersionType.TokenVersionType1_NFT_Child ];
-        if(!supportedTypes.includes(slpMsg.versionType))
-            throw Error('Unsupported token type: ' + slpMsg.versionType);
-        if(chunks.length === 2)
-            throw Error('Missing SLP transaction type');
+        if (!supportedTypes.includes(slpMsg.versionType)) {
+            throw Error("Unsupported token type: " + slpMsg.versionType);
+        }
+        if (chunks.length === 2) {
+            throw Error("Missing SLP transaction type");
+        }
         try {
-            let msgType: string = chunks[2]!.toString('ascii')
-            slpMsg.transactionType = SlpTransactionType[msgType as keyof typeof SlpTransactionType]
-        } catch(_){
-            throw Error('Bad transaction type');
-        }
-        if(slpMsg.transactionType === SlpTransactionType.GENESIS) {
-            if(chunks.length !== 10)
-                throw Error('GENESIS with incorrect number of parameters');
-            slpMsg.symbol = chunks[3] ? chunks[3]!.toString('utf8') : '';
-            slpMsg.name = chunks[4] ? chunks[4]!.toString('utf8') : '';
-            slpMsg.documentUri = chunks[5] ? chunks[5]!.toString('utf8') : '';
+            const msgType: string = chunks[2]!.toString("latin1");
+            slpMsg.transactionType = SlpTransactionType[msgType as keyof typeof SlpTransactionType];
+        } catch (_) {
+            throw Error("Bad transaction type");
+        }
+        if (slpMsg.transactionType === SlpTransactionType.GENESIS) {
+            if (chunks.length !== 10) {
+                throw Error("GENESIS with incorrect number of parameters");
+            }
+            slpMsg.symbol = chunks[3] ? chunks[3]!.toString("utf8") : "";
+            slpMsg.name = chunks[4] ? chunks[4]!.toString("utf8") : "";
+            slpMsg.documentUri = chunks[5] ? chunks[5]!.toString("utf8") : "";
             slpMsg.documentSha256 = chunks[6] ? chunks[6] : null;
-            if(slpMsg.documentSha256) {
-                if(slpMsg.documentSha256.length !== 0 && slpMsg.documentSha256.length !== 32)
+            if (slpMsg.documentSha256) {
+                if (slpMsg.documentSha256.length !== 0 && slpMsg.documentSha256.length !== 32) {
                     throw Error('Token document hash is incorrect length');
+                }
+            }
+            if (!chunks[7]) {
+                throw Error("Bad decimals buffer");
+            }
+            slpMsg.decimals = ( Slp.parseChunkToInt(chunks[7]!, 1, 1, true) as number);
+            if (slpMsg.versionType === 0x41 && slpMsg.decimals !== 0) {
+                throw Error("NFT1 child token must have divisibility set to 0 decimal places.");
+            }
+            if (slpMsg.decimals > 9) {
+                throw Error("Too many decimals");
             }
-            if(!chunks[7])
-                throw Error("Bad decimals buffer")
-            slpMsg.decimals = <number>Slp.parseChunkToInt(chunks[7]!, 1, 1, true);
-            if(slpMsg.versionType === 0x41 && slpMsg.decimals !== 0)
-                throw Error('NFT1 child token must have divisibility set to 0 decimal places.')
-            if(slpMsg.decimals > 9)
-                throw Error('Too many decimals')
             slpMsg.batonVout = chunks[8] ? Slp.parseChunkToInt(chunks[8]!, 1, 1) : null;
-            if(slpMsg.batonVout !== null) {
-                if (slpMsg.batonVout < 2)
-                    throw Error('Mint baton cannot be on vout=0 or 1');
+            if (slpMsg.batonVout !== null) {
+                if (slpMsg.batonVout < 2) {
+                    throw Error("Mint baton cannot be on vout=0 or 1");
+                }
                 slpMsg.containsBaton = true;
             }
-            if(slpMsg.versionType === 0x41 && slpMsg.batonVout !== null)
-                throw Error("NFT1 child token must not have a minting baton!")
-            if(!chunks[9])
-                throw Error("Bad Genesis quantity buffer")
-            if(chunks[9]!.length !== 8)
-                throw Error("Genesis quantity must be provided as an 8-byte buffer")
-            slpMsg.genesisOrMintQuantity = Utils.buffer2BigNumber(chunks[9]!);                
-            if(slpMsg.versionType === 0x41 && !slpMsg.genesisOrMintQuantity.isEqualTo(1))
-                throw Error("NFT1 child token must have GENESIS quantity of 1.")
-        }
-        else if(slpMsg.transactionType === SlpTransactionType.SEND) {
-            if(chunks.length < 4)
-                throw Error('SEND with too few parameters');
-            if(!chunks[3])
-                throw Error("Bad tokenId buffer")
-            if(chunks[3]!.length !== 32)
-                throw Error('token_id is wrong length');
-            slpMsg.tokenIdHex = chunks[3]!.toString('hex');
+            if (slpMsg.versionType === 0x41 && slpMsg.batonVout !== null) {
+                throw Error("NFT1 child token must not have a minting baton!");
+            }
+            if (!chunks[9]) {
+                throw Error("Bad Genesis quantity buffer");
+            }
+            if (chunks[9]!.length !== 8) {
+                throw Error("Genesis quantity must be provided as an 8-byte buffer");
+            }
+            slpMsg.genesisOrMintQuantity = Utils.buffer2BigNumber(chunks[9]!);
+            if (slpMsg.versionType === 0x41 && !slpMsg.genesisOrMintQuantity.isEqualTo(1)) {
+                throw Error("NFT1 child token must have GENESIS quantity of 1.");
+            }
+        } else if (slpMsg.transactionType === SlpTransactionType.SEND) {
+            if (chunks.length < 4) {
+                throw Error("SEND with too few parameters");
+            }
+            if (!chunks[3]) {
+                throw Error("Bad tokenId buffer");
+            }
+            if (chunks[3]!.length !== 32) {
+                throw Error("token_id is wrong length");
+            }
+            slpMsg.tokenIdHex = chunks[3]!.toString("hex");
             // # Note that we put an explicit 0 for  ['token_output'][0] since it
             // # corresponds to vout=0, which is the OP_RETURN tx output.
             // # ['token_output'][1] is the first token output given by the SLP
@@ -752,234 +887,229 @@ export class Slp {
             // # spec, which goes to tx output vout=1.
             slpMsg.sendOutputs = [];
             slpMsg.sendOutputs.push(new BigNumber(0));
-            chunks.slice(4).forEach(chunk => {
-                if(!chunk)
-                    throw Error("Bad send quantity buffer.")
-                if(chunk.length !== 8)
-                    throw Error('SEND quantities must be 8-bytes each.');
+            chunks.slice(4).forEach((chunk) => {
+                if (!chunk) {
+                    throw Error("Bad send quantity buffer.");
+                }
+                if (chunk.length !== 8) {
+                    throw Error("SEND quantities must be 8-bytes each.");
+                }
                 slpMsg.sendOutputs!.push(Utils.buffer2BigNumber(chunk));
             });
             // # maximum 19 allowed token outputs, plus 1 for the explicit [0] we inserted.
-            if(slpMsg.sendOutputs.length < 2)
-                throw Error('Missing output amounts');
-            if(slpMsg.sendOutputs.length > 20)
-                throw Error('More than 19 output amounts');
-        }
-        else if(slpMsg.transactionType === SlpTransactionType.MINT) {
-            if(slpMsg.versionType === 0x41)
-                throw Error("NFT1 Child cannot have MINT transaction type.")
-            if(chunks.length != 6)
-                throw Error('MINT with incorrect number of parameters');
-            if(!chunks[3])
+            if (slpMsg.sendOutputs.length < 2) {
+                throw Error("Missing output amounts");
+            }
+            if (slpMsg.sendOutputs.length > 20) {
+                throw Error("More than 19 output amounts");
+            }
+        } else if (slpMsg.transactionType === SlpTransactionType.MINT) {
+            if (slpMsg.versionType === 0x41) {
+                throw Error("NFT1 Child cannot have MINT transaction type.");
+            }
+            if (chunks.length != 6) {
+                throw Error("MINT with incorrect number of parameters");
+            }
+            if (!chunks[3]) {
                 throw Error("Bad token_id buffer");
-            if(chunks[3]!.length != 32)
-                throw Error('token_id is wrong length');
-            slpMsg.tokenIdHex = chunks[3]!.toString('hex');
-            slpMsg.batonVout = chunks[4] ? Slp.parseChunkToInt(chunks[4]!,1,1) : null;
-            if(slpMsg.batonVout !== null && slpMsg.batonVout !== undefined) {
-                if(slpMsg.batonVout < 2)
-                    throw Error('Mint baton cannot be on vout=0 or 1');
+            }
+            if (chunks[3]!.length != 32) {
+                throw Error("token_id is wrong length");
+            }
+            slpMsg.tokenIdHex = chunks[3]!.toString("hex");
+            slpMsg.batonVout = chunks[4] ? Slp.parseChunkToInt(chunks[4]!, 1, 1) : null;
+            if (slpMsg.batonVout !== null && slpMsg.batonVout !== undefined) {
+                if (slpMsg.batonVout < 2) {
+                    throw Error("Mint baton cannot be on vout=0 or 1");
+                }
                 slpMsg.containsBaton = true;
             }
-            if(!chunks[5])
-                throw Error("Bad Mint quantity buffer")
-            if(chunks[5]!.length !== 8)
-                throw Error("Mint quantity must be provided as an 8-byte buffer")
+            if (!chunks[5]) {
+                throw Error("Bad Mint quantity buffer");
+            }
+            if (chunks[5]!.length !== 8) {
+                throw Error("Mint quantity must be provided as an 8-byte buffer");
+            }
             slpMsg.genesisOrMintQuantity = Utils.buffer2BigNumber(chunks[5]!);
-        }
-        else
+        } else {
             throw Error("Bad transaction type");
+        }
 
-        if(!slpMsg.genesisOrMintQuantity && (!slpMsg.sendOutputs || slpMsg.sendOutputs.length === 0))
+        if (!slpMsg.genesisOrMintQuantity && (!slpMsg.sendOutputs || slpMsg.sendOutputs.length === 0)) {
             throw Error("SLP message must have either Genesis/Mint outputs or Send outputs, both are missing");
+        }
 
         return slpMsg;
     }
- 
-    static parseChunkToInt(intBytes: Buffer, minByteLen: number, maxByteLen: number, raise_on_Null = false) {
-        // # Parse data as unsigned-big-endian encoded integer.
-        // # For empty data different possibilities may occur:
-        // #      minByteLen <= 0 : return 0
-        // #      raise_on_Null == False and minByteLen > 0: return None
-        // #      raise_on_Null == True and minByteLen > 0:  raise SlpInvalidOutputMessage
-        if(intBytes.length >= minByteLen && intBytes.length <= maxByteLen)
-            return intBytes.readUIntBE(0, intBytes.length)
-        if(intBytes.length === 0 && !raise_on_Null)
-            return null;
-        throw Error('Field has wrong length');
-    }
 
     // get list of data chunks resulting from data push operations
-    parseOpReturnToChunks(script: Buffer, allow_op_0=false, allow_op_number=false) {
+    public parseOpReturnToChunks(script: Buffer, allow_op_0= false, allow_op_number= false) {
         // """Extract pushed bytes after opreturn. Returns list of bytes() objects,
         // one per push.
         let ops: PushDataOperation[];
-    
+
         // Strict refusal of non-push opcodes; bad scripts throw OpreturnError."""
         try {
             ops = this.getScriptOperations(script);
-        } catch(e) {
-            //console.log(e);
-            throw Error('Script error');
+        } catch (e) {
+            // console.log(e);
+            throw Error("Script error");
         }
 
-        if(ops[0].opcode !== this.BITBOX.Script.opcodes.OP_RETURN)
+        if (ops[0].opcode !== this.BITBOX.Script.opcodes.OP_RETURN) {
             throw Error('No OP_RETURN');
-        let chunks: (Buffer|null)[] = [];
-        ops.slice(1).forEach(opitem => {
-            if(opitem.opcode > this.BITBOX.Script.opcodes.OP_16)
+        }
+        const chunks: Array<Buffer|null> = [];
+        ops.slice(1).forEach((opitem) => {
+            if (opitem.opcode > this.BITBOX.Script.opcodes.OP_16) {
                 throw Error("Non-push opcode");
-            if(opitem.opcode > this.BITBOX.Script.opcodes.OP_PUSHDATA4) {
-                if(opitem.opcode === 80)
+            }
+            if (opitem.opcode > this.BITBOX.Script.opcodes.OP_PUSHDATA4) {
+                if (opitem.opcode === 80) {
                     throw Error('Non-push opcode');
-                if(!allow_op_number)
+                }
+                if (!allow_op_number) {
                     throw Error('OP_1NEGATE to OP_16 not allowed');
-                if(opitem.opcode === this.BITBOX.Script.opcodes.OP_1NEGATE)
+                }
+                if (opitem.opcode === this.BITBOX.Script.opcodes.OP_1NEGATE) {
                     opitem.data = Buffer.from([0x81]);
-                else // OP_1 - OP_16
+                }
+                else { // OP_1 - OP_16
                     opitem.data = Buffer.from([opitem.opcode - 80]);
+                }
             }
-            if(opitem.opcode === this.BITBOX.Script.opcodes.OP_0 && !allow_op_0){
-                throw Error('OP_0 not allowed');
+            if (opitem.opcode === this.BITBOX.Script.opcodes.OP_0 && !allow_op_0) {
+                throw Error("OP_0 not allowed");
             }
-            chunks.push(opitem.data)
+            chunks.push(opitem.data);
         });
-        //console.log(chunks);
-        return chunks
+        // console.log(chunks);
+        return chunks;
     }
 
     // Get a list of operations with accompanying push data (if a push opcode)
-    getScriptOperations(script: Buffer) {
-        let ops: PushDataOperation[] = [];
+    public getScriptOperations(script: Buffer) {
+        const ops: PushDataOperation[] = [];
         try {
             let n = 0;
             let dlen: number;
             while (n < script.length) {
-                let op: PushDataOperation = { opcode: script[n], data: null }
+                const op: PushDataOperation = { opcode: script[n], data: null };
                 n += 1;
-                if(op.opcode <= this.BITBOX.Script.opcodes.OP_PUSHDATA4) {
-                    if(op.opcode < this.BITBOX.Script.opcodes.OP_PUSHDATA1)
+                if (op.opcode <= this.BITBOX.Script.opcodes.OP_PUSHDATA4) {
+                    if (op.opcode < this.BITBOX.Script.opcodes.OP_PUSHDATA1) {
                         dlen = op.opcode;
-                    else if(op.opcode === this.BITBOX.Script.opcodes.OP_PUSHDATA1) {
+                    }
+                    else if (op.opcode === this.BITBOX.Script.opcodes.OP_PUSHDATA1) {
                         dlen = script[n];
                         n += 1;
-                    }
-                    else if(op.opcode === this.BITBOX.Script.opcodes.OP_PUSHDATA2) {
-                        dlen = script.slice(n, n + 2).readUIntLE(0,2);
+                    } else if (op.opcode === this.BITBOX.Script.opcodes.OP_PUSHDATA2) {
+                        dlen = script.slice(n, n + 2).readUIntLE(0, 2);
                         n += 2;
-                    }
-                    else {
-                        dlen = script.slice(n, n + 4).readUIntLE(0,4);
+                    } else {
+                        dlen = script.slice(n, n + 4).readUIntLE(0, 4);
                         n += 4;
                     }
-                    if((n + dlen) > script.length) {
-                        throw Error('IndexError');
+                    if ((n + dlen) > script.length) {
+                        throw Error("IndexError");
                     }
-                    if(dlen > 0)
+                    if (dlen > 0) {
                         op.data = script.slice(n, n + dlen);
-                    n += dlen
+                    }
+                    n += dlen;
                 }
                 ops.push(op);
             }
-        } catch(e) {
-            //console.log(e);
-            throw Error('truncated script')
+        } catch (e) {
+            // console.log(e);
+            throw Error("truncated script");
         }
         return ops;
     }
 
-    calculateGenesisCost(genesisOpReturnLength: number, inputUtxoSize: number, batonAddress: string|null, bchChangeAddress?: string, feeRate = 1) {
-        return this.calculateMintOrGenesisCost(genesisOpReturnLength, inputUtxoSize, batonAddress, bchChangeAddress, feeRate);
+    public calculateGenesisCost(genesisOpReturnLength: number, inputUtxoSize: number,
+                                batonAddress: string | null, bchChangeAddress?: string, feeRate = 1) {
+        return this.calculateMintOrGenesisCost(genesisOpReturnLength,
+            inputUtxoSize, batonAddress, bchChangeAddress, feeRate);
     }
 
-    calculateMintCost(mintOpReturnLength: number, inputUtxoSize: number, batonAddress: string|null, bchChangeAddress?: string, feeRate = 1) {
-        return this.calculateMintOrGenesisCost(mintOpReturnLength, inputUtxoSize, batonAddress, bchChangeAddress, feeRate);
+    public calculateMintCost(mintOpReturnLength: number, inputUtxoSize: number,
+                             batonAddress: string | null, bchChangeAddress?: string, feeRate = 1) {
+        return this.calculateMintOrGenesisCost(mintOpReturnLength, inputUtxoSize,
+            batonAddress, bchChangeAddress, feeRate);
     }
 
-    calculateMintOrGenesisCost(mintOpReturnLength: number, inputUtxoSize: number, batonAddress: string|null, bchChangeAddress?: string, feeRate: number = 1) {
-        let outputs = 1
-        let nonfeeoutputs = 546
+    public calculateMintOrGenesisCost(mintOpReturnLength: number, inputUtxoSize: number,
+                                      batonAddress: string|null, bchChangeAddress?: string, feeRate: number = 1) {
+        let outputs = 1;
+        let nonfeeoutputs = 546;
         if (batonAddress !== null && batonAddress !== undefined) {
-            nonfeeoutputs += 546
-            outputs += 1
+            nonfeeoutputs += 546;
+            outputs += 1;
         }
 
         if (bchChangeAddress !== null && bchChangeAddress !== undefined) {
-            outputs += 1
+            outputs += 1;
         }
 
-        let fee = this.BITBOX.BitcoinCash.getByteCount({ P2PKH: inputUtxoSize }, { P2PKH: outputs })
-        fee += mintOpReturnLength
-        fee += 10 // added to account for OP_RETURN ammount of 0000000000000000
-        fee *= feeRate
-        //console.log("MINT/GENESIS cost before outputs: " + fee.toString());
-        fee += nonfeeoutputs
-        //console.log("MINT/GENESIS cost after outputs are added: " + fee.toString());
-        return fee
+        let fee = this.BITBOX.BitcoinCash.getByteCount({ P2PKH: inputUtxoSize }, { P2PKH: outputs });
+        fee += mintOpReturnLength;
+        fee += 10; // added to account for OP_RETURN ammount of 0000000000000000
+        fee *= feeRate;
+        // console.log("MINT/GENESIS cost before outputs: " + fee.toString());
+        fee += nonfeeoutputs;
+        // console.log("MINT/GENESIS cost after outputs are added: " + fee.toString());
+        return fee;
     }
 
-    calculateSendCost(sendOpReturnLength: number, inputUtxoSize: number, outputAddressArraySize: number, bchChangeAddress?: string, feeRate = 1, forTokens=true) {
+    public calculateSendCost(sendOpReturnLength: number, inputUtxoSize: number, 
+                             outputAddressArraySize: number, bchChangeAddress?: string, feeRate = 1, forTokens= true) {
         let outputs = outputAddressArraySize;
         let nonfeeoutputs = 0;
-        if(forTokens)
+        if (forTokens) {
             nonfeeoutputs = outputAddressArraySize * 546;
+        }
 
         if (bchChangeAddress !== null && bchChangeAddress !== undefined) {
-            outputs += 1
+            outputs += 1;
         }
 
-        let fee = this.BITBOX.BitcoinCash.getByteCount({ P2PKH: inputUtxoSize }, { P2PKH: outputs })
-        fee += sendOpReturnLength
-        fee += 10 // added to account for OP_RETURN ammount of 0000000000000000
-        fee *= feeRate
-        //console.log("SEND cost before outputs: " + fee.toString());
-        fee += nonfeeoutputs
-        //console.log("SEND cost after outputs are added: " + fee.toString());
-        return fee
+        let fee = this.BITBOX.BitcoinCash.getByteCount({ P2PKH: inputUtxoSize }, { P2PKH: outputs });
+        fee += sendOpReturnLength;
+        fee += 10; // added to account for OP_RETURN ammount of 0000000000000000
+        fee *= feeRate;
+        // console.log("SEND cost before outputs: " + fee.toString());
+        fee += nonfeeoutputs;
+        // console.log("SEND cost after outputs are added: " + fee.toString());
+        return fee;
     }
 
-    static preSendSlpJudgementCheck(txo: SlpAddressUtxoResult, tokenId: string){
-        if (txo.slpUtxoJudgement === undefined || txo.slpUtxoJudgement === null || txo.slpUtxoJudgement === SlpUtxoJudgement.UNKNOWN)
-            throw Error("There at least one input UTXO that does not have a proper SLP judgement")
-        if (txo.slpUtxoJudgement === SlpUtxoJudgement.UNSUPPORTED_TYPE)
-            throw Error("There is at least one input UTXO that is an Unsupported SLP type.")
-        if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON)
-            throw Error("There is at least one input UTXO that is a baton.  You can only spend batons in a MINT transaction.")
-        if (txo.slpTransactionDetails) {
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
-                if(!txo.slpUtxoJudgementAmount)
-                    throw Error("There is at least one input token that does not have the 'slpUtxoJudgementAmount' property set.")
-                if(txo.slpTransactionDetails.tokenIdHex !== tokenId)
-                    throw Error("There is at least one input UTXO that is a different SLP token than the one specified.")
-                return txo.slpTransactionDetails.tokenIdHex === tokenId;
-            }
-        } 
-        return false;
-    }
+    public async processUtxosForSlpAbstract(utxos: SlpAddressUtxoResult[], asyncSlpValidator: SlpValidator) {
 
-    async processUtxosForSlpAbstract(utxos: SlpAddressUtxoResult[], asyncSlpValidator: SlpValidator) {
-        
         // 1) parse SLP OP_RETURN and cast initial SLP judgement, based on OP_RETURN only.
-        for(let txo of utxos) {
+        for (const txo of utxos) {
             this.applyInitialSlpJudgement(txo);
-            if(txo.slpUtxoJudgement === SlpUtxoJudgement.UNKNOWN || txo.slpUtxoJudgement === undefined)
-                throw Error('Utxo SLP judgement has not been set, unknown error.');
+            if (txo.slpUtxoJudgement === SlpUtxoJudgement.UNKNOWN || txo.slpUtxoJudgement === undefined) {
+                throw Error("Utxo SLP judgement has not been set, unknown error.");
+            }
         }
-    
+
         // 2) Cast final SLP judgement using the supplied async validator
         await this.applyFinalSlpJudgement(asyncSlpValidator, utxos);
-        
+
         // 3) Prepare results object
         const result: SlpBalancesResult = this.computeSlpBalances(utxos);
-    
+
         // 4) Check that all UTXOs have been categorized
         let tokenTxoCount = 0;
-        for(let id in result.slpTokenUtxos) tokenTxoCount += result.slpTokenUtxos[id].length;
+        for (const id in result.slpTokenUtxos) { tokenTxoCount += result.slpTokenUtxos[id].length; }
         let batonTxoCount = 0;
-        for(let id in result.slpBatonUtxos) batonTxoCount += result.slpBatonUtxos[id].length;
-        if(utxos.length !== (tokenTxoCount + batonTxoCount + result.nonSlpUtxos.length + result.unknownTokenTypeUtxos.length + result.invalidBatonUtxos.length + result.invalidTokenUtxos.length))
-            throw Error('Not all UTXOs have been categorized. Unknown Error.');
-    
+        for (const id in result.slpBatonUtxos) { batonTxoCount += result.slpBatonUtxos[id].length; }
+        if (utxos.length !== (tokenTxoCount + batonTxoCount + result.nonSlpUtxos.length +
+            result.unknownTokenTypeUtxos.length + result.invalidBatonUtxos.length + result.invalidTokenUtxos.length)) {
+            throw Error("Not all UTXOs have been categorized. Unknown Error.");
+        }
+
         return result;
     }
 
@@ -997,63 +1127,65 @@ export class Slp {
             slpBatonUtxos: {},
             nonSlpUtxos: [],
             invalidTokenUtxos: [],
-            invalidBatonUtxos: [], 
-            unknownTokenTypeUtxos: []
+            invalidBatonUtxos: [],
+            unknownTokenTypeUtxos: [],
         };
         // 5) Loop through UTXO set and accumulate balances for type of utxo, organize the Utxos into their categories.
         for (const txo of utxos) {
             if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
-                if (!(txo.slpTransactionDetails.tokenIdHex in result.slpTokenBalances))
+                if (!(txo.slpTransactionDetails.tokenIdHex in result.slpTokenBalances)) {
                     result.slpTokenBalances[txo.slpTransactionDetails.tokenIdHex] = new BigNumber(0);
-                if (txo.slpTransactionDetails.transactionType === SlpTransactionType.GENESIS || txo.slpTransactionDetails.transactionType === SlpTransactionType.MINT) {
-                    result.slpTokenBalances[txo.slpTransactionDetails.tokenIdHex] = result.slpTokenBalances[txo.slpTransactionDetails.tokenIdHex].plus(<BigNumber>txo.slpTransactionDetails.genesisOrMintQuantity);
                 }
-                else if (txo.slpTransactionDetails.transactionType === SlpTransactionType.SEND && txo.slpTransactionDetails.sendOutputs) {
-                    let qty = txo.slpTransactionDetails.sendOutputs[txo.vout];
-                    result.slpTokenBalances[txo.slpTransactionDetails.tokenIdHex] = result.slpTokenBalances[txo.slpTransactionDetails.tokenIdHex].plus(qty);
-                }
-                else {
-                    throw Error('Unknown Error: cannot have an SLP_TOKEN that is not from GENESIS, MINT, or SEND.');
+                if (txo.slpTransactionDetails.transactionType === SlpTransactionType.GENESIS ||
+                    txo.slpTransactionDetails.transactionType === SlpTransactionType.MINT) {
+                    result.slpTokenBalances[txo.slpTransactionDetails.tokenIdHex] =
+                    result.slpTokenBalances[txo.slpTransactionDetails.tokenIdHex]
+                    .plus( txo.slpTransactionDetails.genesisOrMintQuantity as BigNumber);
+                } else if (txo.slpTransactionDetails.transactionType === SlpTransactionType.SEND &&
+                    txo.slpTransactionDetails.sendOutputs) {
+                    const qty = txo.slpTransactionDetails.sendOutputs[txo.vout];
+                    result.slpTokenBalances[txo.slpTransactionDetails.tokenIdHex] =
+                    result.slpTokenBalances[txo.slpTransactionDetails.tokenIdHex].plus(qty);
+                } else {
+                    throw Error("Unknown Error: cannot have an SLP_TOKEN that is not from GENESIS, MINT, or SEND.");
                 }
                 result.satoshis_in_slp_token += txo.satoshis;
-                if(!(txo.slpTransactionDetails.tokenIdHex in result.slpTokenUtxos))
+                if (!(txo.slpTransactionDetails.tokenIdHex in result.slpTokenUtxos)) {
                     result.slpTokenUtxos[txo.slpTransactionDetails.tokenIdHex] = [];
+                }
 
                 // NFT1 Children Balances (nftParentChildBalances):
-                if(txo.slpTransactionDetails.versionType === SlpVersionType.TokenVersionType1_NFT_Child) {
-                    if(!(txo.nftParentId! in result.nftParentChildBalances)) {
+                if (txo.slpTransactionDetails.versionType === SlpVersionType.TokenVersionType1_NFT_Child) {
+                    if (!(txo.nftParentId! in result.nftParentChildBalances)) {
                         result.nftParentChildBalances[txo.nftParentId!] = {};
                     }
-                    if(!(txo.slpTransactionDetails.tokenIdHex in result.nftParentChildBalances[txo.nftParentId!])) {
-                        result.nftParentChildBalances[txo.nftParentId!][txo.slpTransactionDetails.tokenIdHex] = txo.slpUtxoJudgementAmount;
+                    if (!(txo.slpTransactionDetails.tokenIdHex in result.nftParentChildBalances[txo.nftParentId!])) {
+                        result.nftParentChildBalances[txo.nftParentId!][txo.slpTransactionDetails.tokenIdHex] =
+                        txo.slpUtxoJudgementAmount;
                     } else {
                         // NOTE: this does not cover the 0 quantity SEND case
-                        throw Error("Cannot have 2 UTXOs with the same NFT1 child token designation.")
+                        throw Error("Cannot have 2 UTXOs with the same NFT1 child token designation.");
                     }
                 }
 
                 // All token balances (includes Type 1, and NFT1(65/129)):
                 result.slpTokenUtxos[txo.slpTransactionDetails.tokenIdHex].push(txo);
-            }
-            else if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
+            } else if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
                 result.satoshis_in_slp_baton += txo.satoshis;
-                if(!(txo.slpTransactionDetails.tokenIdHex in result.slpBatonUtxos))
+                if (!(txo.slpTransactionDetails.tokenIdHex in result.slpBatonUtxos)) {
                     result.slpBatonUtxos[txo.slpTransactionDetails.tokenIdHex] = [];
+                }
                 result.slpBatonUtxos[txo.slpTransactionDetails.tokenIdHex].push(txo);
-            }
-            else if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG) {
+            } else if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG) {
                 result.satoshis_in_invalid_token_dag += txo.satoshis;
                 result.invalidTokenUtxos.push(txo);
-            }
-            else if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG) {
+            } else if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG) {
                 result.satoshis_in_invalid_baton_dag += txo.satoshis;
                 result.invalidBatonUtxos.push(txo);
-            }
-            else if (txo.slpUtxoJudgement === SlpUtxoJudgement.UNSUPPORTED_TYPE) {
+            } else if (txo.slpUtxoJudgement === SlpUtxoJudgement.UNSUPPORTED_TYPE) {
                 result.satoshis_in_unknown_token_type += txo.satoshis;
                 result.unknownTokenTypeUtxos.push(txo);
-            }
-            else {
+            } else {
                 result.satoshis_available_bch += txo.satoshis;
                 result.nonSlpUtxos.push(txo);
             }
@@ -1063,93 +1195,94 @@ export class Slp {
 
     private applyInitialSlpJudgement(txo: SlpAddressUtxoResult) {
         try {
-            let vout = txo.tx.vout.find(vout => vout.n === 0);
-            if (!vout)
+            const vout = txo.tx.vout.find(vout => vout.n === 0);
+            if (!vout) {
                 throw 'Utxo contains no Vout!';
-            let vout0script = Buffer.from(vout.scriptPubKey.hex, 'hex');
+            }
+            const vout0script = Buffer.from(vout.scriptPubKey.hex, "hex");
             txo.slpTransactionDetails = this.parseSlpOutputScript(vout0script);
 
             // populate txid for GENESIS
-            if (txo.slpTransactionDetails.transactionType === SlpTransactionType.GENESIS)
+            if (txo.slpTransactionDetails.transactionType === SlpTransactionType.GENESIS) {
                 txo.slpTransactionDetails.tokenIdHex = txo.txid;
+            }
 
-            // apply initial SLP judgement to the UTXO (based on OP_RETURN parsing ONLY! Still need to validate the DAG for possible tokens and batons!)
+            // apply initial SLP judgement to the UTXO (based on OP_RETURN 
+            // parsing ONLY! Still need to validate the DAG for possible tokens and batons!)
             if (txo.slpTransactionDetails.transactionType === SlpTransactionType.GENESIS ||
                 txo.slpTransactionDetails.transactionType === SlpTransactionType.MINT) {
                 if (txo.slpTransactionDetails.containsBaton && txo.slpTransactionDetails.batonVout === txo.vout) {
                     txo.slpUtxoJudgement = SlpUtxoJudgement.SLP_BATON;
-                }
-                else if (txo.vout === 1 && txo.slpTransactionDetails.genesisOrMintQuantity!.isGreaterThan(0)) {
+                } else if (txo.vout === 1 && txo.slpTransactionDetails.genesisOrMintQuantity!.isGreaterThan(0)) {
                     txo.slpUtxoJudgement = SlpUtxoJudgement.SLP_TOKEN;
-                    txo.slpUtxoJudgementAmount = <BigNumber>txo.slpTransactionDetails.genesisOrMintQuantity;
-                }
-                else
+                    txo.slpUtxoJudgementAmount = ( txo.slpTransactionDetails.genesisOrMintQuantity as BigNumber);
+                } else {
                     txo.slpUtxoJudgement = SlpUtxoJudgement.NOT_SLP;
-            }
-            else if (txo.slpTransactionDetails.transactionType === SlpTransactionType.SEND && txo.slpTransactionDetails.sendOutputs) {
+ }
+            } else if (txo.slpTransactionDetails.transactionType === SlpTransactionType.SEND &&
+                txo.slpTransactionDetails.sendOutputs) {
                 if (txo.vout > 0 && txo.vout < txo.slpTransactionDetails.sendOutputs.length) {
                     txo.slpUtxoJudgement = SlpUtxoJudgement.SLP_TOKEN;
                     txo.slpUtxoJudgementAmount = txo.slpTransactionDetails.sendOutputs[txo.vout];
-                }
-                else
+                } else {
                     txo.slpUtxoJudgement = SlpUtxoJudgement.NOT_SLP;
+                }
             } else {
                 txo.slpUtxoJudgement = SlpUtxoJudgement.NOT_SLP;
             }
-        }
-        catch (e) {
-            if(e.message.includes("Unsupported token type"))
-                txo.slpUtxoJudgement = SlpUtxoJudgement.UNSUPPORTED_TYPE
-            else
+        } catch (e) {
+            if (e.message.includes("Unsupported token type")) {
+                txo.slpUtxoJudgement = SlpUtxoJudgement.UNSUPPORTED_TYPE;
+            } else {
                 txo.slpUtxoJudgement = SlpUtxoJudgement.NOT_SLP;
+            }
         }
     }
 
     private async applyFinalSlpJudgement(asyncSlpValidator: SlpValidator, utxos: SlpAddressUtxoResult[]) {
 
-        let validSLPTx: string[] = await asyncSlpValidator.validateSlpTransactions([
-            ...new Set(utxos.filter(txOut => {
+        const validSLPTx: string[] = await asyncSlpValidator.validateSlpTransactions([
+            ...new Set(utxos.filter((txOut) => {
                 if (txOut.slpTransactionDetails &&
                     txOut.slpUtxoJudgement !== SlpUtxoJudgement.UNKNOWN &&
                     txOut.slpUtxoJudgement !== SlpUtxoJudgement.UNSUPPORTED_TYPE &&
-                    txOut.slpUtxoJudgement !== SlpUtxoJudgement.NOT_SLP)
+                    txOut.slpUtxoJudgement !== SlpUtxoJudgement.NOT_SLP) {
                     return true;
+                }
                 return false;
-            }).map(txOut => txOut.txid))
+            }).map((txOut) => txOut.txid)),
         ]);
 
         utxos.forEach(utxo => {
             if (!(validSLPTx.includes(utxo.txid))) {
                 if (utxo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
                     utxo.slpUtxoJudgement = SlpUtxoJudgement.INVALID_TOKEN_DAG;
-                }
-                else if (utxo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
+                } else if (utxo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
                     utxo.slpUtxoJudgement = SlpUtxoJudgement.INVALID_BATON_DAG;
                 }
             }
         });
 
         // function for determination of NFT1 parent ID
-        let getNftParentId = async (tokenIdHex: string) => {
-            let txnhex = (await asyncSlpValidator.getRawTransactions([tokenIdHex]))[0];
-            let tx = Primatives.Transaction.parseFromBuffer(Buffer.from(txnhex, 'hex'));
-            let nftBurnTxnHex = (await asyncSlpValidator.getRawTransactions([tx.inputs[0].previousTxHash]))[0];
-            let nftBurnTxn = Primatives.Transaction.parseFromBuffer(Buffer.from(nftBurnTxnHex, 'hex'));
-            let slp = new Slp(this.BITBOX);
-            let nftBurnSlp = slp.parseSlpOutputScript(Buffer.from(nftBurnTxn.outputs[0].scriptPubKey));
+        const getNftParentId = async (tokenIdHex: string) => {
+            const txnhex = (await asyncSlpValidator.getRawTransactions([tokenIdHex]))[0];
+            const tx = Primatives.Transaction.parseFromBuffer(Buffer.from(txnhex, "hex"));
+            const nftBurnTxnHex = (await asyncSlpValidator.getRawTransactions([tx.inputs[0].previousTxHash]))[0];
+            const nftBurnTxn = Primatives.Transaction.parseFromBuffer(Buffer.from(nftBurnTxnHex, "hex"));
+            const slp = new Slp(this.BITBOX);
+            const nftBurnSlp = slp.parseSlpOutputScript(Buffer.from(nftBurnTxn.outputs[0].scriptPubKey));
             if (nftBurnSlp.transactionType === SlpTransactionType.GENESIS) {
                 return tx.inputs[0].previousTxHash;
-            }
-            else {
+            } else {
                 return nftBurnSlp.tokenIdHex;
             }
-        }
+        };
 
         // Loop through utxos to add nftParentId to any NFT1 child UTXO.
-        for(let txo in utxos) {
-            if(utxos[txo].slpTransactionDetails && 
+        for (const txo in utxos) {
+            if (utxos[txo].slpTransactionDetails &&
                 utxos[txo].slpTransactionDetails.versionType === SlpVersionType.TokenVersionType1_NFT_Child) {
-                if(utxos[txo].slpTransactionDetails.transactionType !== SlpTransactionType.GENESIS) {
+                if (utxos[txo].slpTransactionDetails.transactionType !== SlpTransactionType.GENESIS) {
                     utxos[txo].nftParentId = await getNftParentId(utxos[txo].slpTransactionDetails.tokenIdHex);
                 } else {
                     utxos[txo].nftParentId = await getNftParentId(utxos[txo].txid);
@@ -1157,4 +1290,4 @@ export class Slp {
             }
         }
     }
-}
\ No newline at end of file
+}

programming_language: TypeScript
filename: package-lock.json
change_type: ModificationType.MODIFY
diff: @@ -1,29 +1,28 @@
 {
   "name": "slpjs",
-  "version": "0.21.3",
+  "version": "0.21.4",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
     "@babel/code-frame": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
-      "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
+      "version": "7.5.5",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
+      "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
       "dev": true,
       "requires": {
         "@babel/highlight": "^7.0.0"
       }
     },
     "@babel/generator": {
-      "version": "7.4.4",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz",
-      "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==",
+      "version": "7.6.4",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz",
+      "integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.4.4",
+        "@babel/types": "^7.6.3",
         "jsesc": "^2.5.1",
-        "lodash": "^4.17.11",
-        "source-map": "^0.5.0",
-        "trim-right": "^1.0.1"
+        "lodash": "^4.17.13",
+        "source-map": "^0.5.0"
       }
     },
     "@babel/helper-function-name": {
@@ -56,9 +55,9 @@
       }
     },
     "@babel/highlight": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
-      "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
+      "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
       "dev": true,
       "requires": {
         "chalk": "^2.0.0",
@@ -67,58 +66,47 @@
       }
     },
     "@babel/parser": {
-      "version": "7.4.5",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz",
-      "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==",
+      "version": "7.6.4",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz",
+      "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==",
       "dev": true
     },
     "@babel/template": {
-      "version": "7.4.4",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz",
-      "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz",
+      "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
-        "@babel/parser": "^7.4.4",
-        "@babel/types": "^7.4.4"
+        "@babel/parser": "^7.6.0",
+        "@babel/types": "^7.6.0"
       }
     },
     "@babel/traverse": {
-      "version": "7.4.5",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz",
-      "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==",
+      "version": "7.6.3",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz",
+      "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.0.0",
-        "@babel/generator": "^7.4.4",
+        "@babel/code-frame": "^7.5.5",
+        "@babel/generator": "^7.6.3",
         "@babel/helper-function-name": "^7.1.0",
         "@babel/helper-split-export-declaration": "^7.4.4",
-        "@babel/parser": "^7.4.5",
-        "@babel/types": "^7.4.4",
+        "@babel/parser": "^7.6.3",
+        "@babel/types": "^7.6.3",
         "debug": "^4.1.0",
         "globals": "^11.1.0",
-        "lodash": "^4.17.11"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "4.1.1",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
-          "dev": true,
-          "requires": {
-            "ms": "^2.1.1"
-          }
-        }
+        "lodash": "^4.17.13"
       }
     },
     "@babel/types": {
-      "version": "7.4.4",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz",
-      "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==",
+      "version": "7.6.3",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz",
+      "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==",
       "dev": true,
       "requires": {
         "esutils": "^2.0.2",
-        "lodash": "^4.17.11",
+        "lodash": "^4.17.13",
         "to-fast-properties": "^2.0.0"
       }
     },
@@ -202,9 +190,9 @@
       }
     },
     "@types/chai": {
-      "version": "4.1.7",
-      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.7.tgz",
-      "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==",
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.4.tgz",
+      "integrity": "sha512-7qvf9F9tMTzo0akeswHPGqgUx/gIaJqrOEET/FCD8CFRkSUHlygQiM5yB6OvjrtdxBVLSyw7COJubsFYs0683g==",
       "dev": true
     },
     "@types/google-protobuf": {
@@ -246,9 +234,9 @@
       }
     },
     "@types/sinon": {
-      "version": "7.0.12",
-      "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.0.12.tgz",
-      "integrity": "sha512-fo0MWpVPSUrnZZhp9wyu+hhI3VJ9+Jhs+PWrokBTg3d2ryNPDOAWF1csIhQuYWBTn7KdZzXpRgpX2o6cwOlPWg==",
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.0.tgz",
+      "integrity": "sha512-NyzhuSBy97B/zE58cDw4NyGvByQbAHNP9069KVSgnXt/sc0T6MFRh0InKAeBVHJWdSXG1S3+PxgVIgKo9mTHbw==",
       "dev": true
     },
     "@types/wif": {
@@ -391,7 +379,7 @@
     },
     "archy": {
       "version": "1.0.0",
-      "resolved": false,
+      "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
       "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
       "dev": true
     },
@@ -490,9 +478,9 @@
       "dev": true
     },
     "async-limiter": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
-      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
       "dev": true
     },
     "asynckit": {
@@ -542,7 +530,7 @@
       "dependencies": {
         "ansi-regex": {
           "version": "2.1.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
           "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
           "dev": true
         },
@@ -573,7 +561,7 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
           "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "dev": true,
           "requires": {
@@ -621,9 +609,9 @@
       "dev": true
     },
     "base64id": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
-      "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+      "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
       "dev": true
     },
     "bc-bip68": {
@@ -888,12 +876,13 @@
       }
     },
     "bitcoinjs-message": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.0.0.tgz",
-      "integrity": "sha512-H5pJC7/eSqVjREiEOZ4jifX+7zXYP3Y28GIOIqg9hrgE7Vj8Eva9+HnVqnxwA1rJPOwZKuw0vo6k0UxgVc6q1A==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.1.0.tgz",
+      "integrity": "sha512-xVL2YvyAJUI8ZwfNpi6Ju5zda3+QYGHTLUvISDb0VHWbsWn9Zyvd1o8XHRC/0r+DNwDIwenpXDSPl1XLCMGnMA==",
       "dev": true,
       "requires": {
-        "bs58check": "^2.0.2",
+        "bech32": "^1.1.3",
+        "bs58check": "^2.1.2",
         "buffer-equals": "^1.0.3",
         "create-hash": "^1.1.2",
         "secp256k1": "^3.0.1",
@@ -1232,15 +1221,6 @@
       "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
       "dev": true
     },
-    "can-promise": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/can-promise/-/can-promise-0.0.1.tgz",
-      "integrity": "sha512-gzVrHyyrvgt0YpDm7pn04MQt8gjh0ZAhN4ZDyCRtGl6YnuuK6b4aiUTD7G52r9l4YNmxfTtEscb92vxtAlL6XQ==",
-      "dev": true,
-      "requires": {
-        "window-or-global": "^1.0.1"
-      }
-    },
     "caseless": {
       "version": "0.12.0",
       "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
@@ -1328,12 +1308,6 @@
       "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
       "dev": true
     },
-    "code-point-at": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-      "dev": true
-    },
     "coininfo": {
       "version": "github:Bitcoin-com/coininfo#eece2c6141d08c3e7783929f2a1e1e681aa1a82c",
       "from": "github:Bitcoin-com/coininfo",
@@ -1393,9 +1367,9 @@
       }
     },
     "commander": {
-      "version": "2.20.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
-      "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
       "dev": true,
       "optional": true
     },
@@ -1583,9 +1557,9 @@
       "dev": true
     },
     "debug": {
-      "version": "3.2.6",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
-      "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+      "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
       "dev": true,
       "requires": {
         "ms": "^2.1.1"
@@ -1798,56 +1772,30 @@
       "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
       "dev": true
     },
-    "end-of-stream": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
-      "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
-      "dev": true,
-      "requires": {
-        "once": "^1.4.0"
-      }
-    },
     "engine.io": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.3.2.tgz",
-      "integrity": "sha512-AsaA9KG7cWPXWHp5FvHdDWY3AMWeZ8x+2pUVLcn71qE5AtAzgGbxuclOytygskw8XGmiQafTmnI9Bix3uihu2w==",
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.0.tgz",
+      "integrity": "sha512-XCyYVWzcHnK5cMz7G4VTu2W7zJS7SM1QkcelghyIk/FmobWBtXE7fwhBusEKvCSqc3bMh8fNFMlUkCKTFRxH2w==",
       "dev": true,
       "requires": {
         "accepts": "~1.3.4",
-        "base64id": "1.0.0",
+        "base64id": "2.0.0",
         "cookie": "0.3.1",
-        "debug": "~3.1.0",
-        "engine.io-parser": "~2.1.0",
-        "ws": "~6.1.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        }
+        "debug": "~4.1.0",
+        "engine.io-parser": "~2.2.0",
+        "ws": "^7.1.2"
       }
     },
     "engine.io-client": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.3.2.tgz",
-      "integrity": "sha512-y0CPINnhMvPuwtqXfsGuWE8BB66+B6wTtCofQDRecMQPYX3MYUZXFNKDhdrSe3EVjgOu4V3rxdeqN/Tr91IgbQ==",
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz",
+      "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==",
       "dev": true,
       "requires": {
         "component-emitter": "1.2.1",
         "component-inherit": "0.0.3",
-        "debug": "~3.1.0",
-        "engine.io-parser": "~2.1.1",
+        "debug": "~4.1.0",
+        "engine.io-parser": "~2.2.0",
         "has-cors": "1.1.0",
         "indexof": "0.0.1",
         "parseqs": "0.0.5",
@@ -1857,27 +1805,21 @@
         "yeast": "0.1.2"
       },
       "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+        "ws": {
+          "version": "6.1.4",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
+          "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
           "dev": true,
           "requires": {
-            "ms": "2.0.0"
+            "async-limiter": "~1.0.0"
           }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
         }
       }
     },
     "engine.io-parser": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
-      "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
+      "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
       "dev": true,
       "requires": {
         "after": "0.8.2",
@@ -1897,17 +1839,21 @@
       }
     },
     "es-abstract": {
-      "version": "1.13.0",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
-      "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
+      "version": "1.16.0",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz",
+      "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==",
       "dev": true,
       "requires": {
         "es-to-primitive": "^1.2.0",
         "function-bind": "^1.1.1",
         "has": "^1.0.3",
+        "has-symbols": "^1.0.0",
         "is-callable": "^1.1.4",
         "is-regex": "^1.0.4",
-        "object-keys": "^1.0.12"
+        "object-inspect": "^1.6.0",
+        "object-keys": "^1.1.1",
+        "string.prototype.trimleft": "^2.1.0",
+        "string.prototype.trimright": "^2.1.0"
       }
     },
     "es-to-primitive": {
@@ -1995,6 +1941,15 @@
             "which": "^1.2.9"
           }
         },
+        "debug": {
+          "version": "3.2.6",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
         "strip-ansi": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
@@ -2046,9 +2001,9 @@
       }
     },
     "eslint-module-utils": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz",
-      "integrity": "sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw==",
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz",
+      "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==",
       "dev": true,
       "requires": {
         "debug": "^2.6.8",
@@ -2316,9 +2271,9 @@
       }
     },
     "eslint-visitor-keys": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
-      "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
+      "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
       "dev": true
     },
     "espree": {
@@ -2356,15 +2311,15 @@
       }
     },
     "estraverse": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
-      "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
       "dev": true
     },
     "esutils": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
-      "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
       "dev": true
     },
     "events": {
@@ -2383,36 +2338,6 @@
         "safe-buffer": "^5.1.1"
       }
     },
-    "execa": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
-      "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
-      "dev": true,
-      "requires": {
-        "cross-spawn": "^6.0.0",
-        "get-stream": "^4.0.0",
-        "is-stream": "^1.1.0",
-        "npm-run-path": "^2.0.0",
-        "p-finally": "^1.0.0",
-        "signal-exit": "^3.0.0",
-        "strip-eof": "^1.0.0"
-      },
-      "dependencies": {
-        "cross-spawn": {
-          "version": "6.0.5",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-          "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
-          "dev": true,
-          "requires": {
-            "nice-try": "^1.0.4",
-            "path-key": "^2.0.1",
-            "semver": "^5.5.0",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        }
-      }
-    },
     "extend": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -2515,6 +2440,17 @@
         "graceful-fs": "^4.1.2",
         "rimraf": "~2.6.2",
         "write": "^0.2.1"
+      },
+      "dependencies": {
+        "rimraf": {
+          "version": "2.6.3",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+          "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        }
       }
     },
     "follow-redirects": {
@@ -2612,15 +2548,6 @@
       "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
       "dev": true
     },
-    "get-stream": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
-      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
-      "dev": true,
-      "requires": {
-        "pump": "^3.0.0"
-      }
-    },
     "getpass": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@@ -2657,9 +2584,9 @@
       "dev": true
     },
     "graceful-fs": {
-      "version": "4.1.15",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
-      "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+      "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
       "dev": true
     },
     "growl": {
@@ -3262,9 +3189,9 @@
       }
     },
     "handlebars": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
-      "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.5.tgz",
+      "integrity": "sha512-0Ce31oWVB7YidkaTq33ZxEbN+UDxMMgThvCe8ptgQViymL5DPis9uLdTA13MiRPhgvqyxIegugrP97iK3JeBHg==",
       "dev": true,
       "requires": {
         "neo-async": "^2.6.0",
@@ -3280,13 +3207,13 @@
           "dev": true
         },
         "uglify-js": {
-          "version": "3.5.15",
-          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.15.tgz",
-          "integrity": "sha512-fe7aYFotptIddkwcm6YuA0HmknBZ52ZzOsUxZEdhhkSsz7RfjHDX2QDxwKTiv4JQ5t5NhfmpgAK+J7LiDhKSqg==",
+          "version": "3.6.4",
+          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.4.tgz",
+          "integrity": "sha512-9Yc2i881pF4BPGhjteCXQNaXx1DCwm3dtOyBaG2hitHjLWOczw/ki8vD1bqyT3u6K0Ms/FpCShkmfg+FtlOfYA==",
           "dev": true,
           "optional": true,
           "requires": {
-            "commander": "~2.20.0",
+            "commander": "~2.20.3",
             "source-map": "~0.6.1"
           }
         }
@@ -3439,9 +3366,9 @@
       }
     },
     "hosted-git-info": {
-      "version": "2.7.1",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
-      "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
+      "version": "2.8.5",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz",
+      "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==",
       "dev": true
     },
     "htmlescape": {
@@ -3591,12 +3518,6 @@
         "xtend": "^4.0.0"
       }
     },
-    "invert-kv": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
-      "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
-      "dev": true
-    },
     "is-arrayish": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -3718,9 +3639,9 @@
       },
       "dependencies": {
         "semver": {
-          "version": "6.1.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.0.tgz",
-          "integrity": "sha512-kCqEOOHoBcFs/2Ccuk4Xarm/KiWRSLEX9CAZF8xkJ6ZPlIoTZ8V5f7J16vYLJqDbR7KrxTJpR2lqjIEm2Qx9cQ==",
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
           "dev": true
         }
       }
@@ -3760,15 +3681,6 @@
         "source-map": "^0.6.1"
       },
       "dependencies": {
-        "debug": {
-          "version": "4.1.1",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
-          "dev": true,
-          "requires": {
-            "ms": "^2.1.1"
-          }
-        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -3884,12 +3796,13 @@
       }
     },
     "jsx-ast-utils": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.1.0.tgz",
-      "integrity": "sha512-yDGDG2DS4JcqhA6blsuYbtsT09xL8AoLuUR2Gb5exrw7UEM19sBcOTq+YBBhrNbl0PUC4R4LnFu+dHg2HKeVvA==",
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz",
+      "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==",
       "dev": true,
       "requires": {
-        "array-includes": "^3.0.3"
+        "array-includes": "^3.0.3",
+        "object.assign": "^4.1.0"
       }
     },
     "keccak": {
@@ -3923,15 +3836,6 @@
         }
       }
     },
-    "lcid": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
-      "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
-      "dev": true,
-      "requires": {
-        "invert-kv": "^2.0.0"
-      }
-    },
     "levn": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@@ -4042,15 +3946,6 @@
       "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
       "dev": true
     },
-    "map-age-cleaner": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
-      "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
-      "dev": true,
-      "requires": {
-        "p-defer": "^1.0.0"
-      }
-    },
     "md5.js": {
       "version": "1.3.5",
       "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -4061,17 +3956,6 @@
         "safe-buffer": "^5.1.2"
       }
     },
-    "mem": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
-      "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
-      "dev": true,
-      "requires": {
-        "map-age-cleaner": "^0.1.1",
-        "mimic-fn": "^2.0.0",
-        "p-is-promise": "^2.0.0"
-      }
-    },
     "merge-source-map": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
@@ -4121,9 +4005,9 @@
       }
     },
     "mimic-fn": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+      "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
       "dev": true
     },
     "minimalistic-assert": {
@@ -4225,6 +4109,24 @@
         }
       }
     },
+    "mock-require": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz",
+      "integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==",
+      "dev": true,
+      "requires": {
+        "get-caller-file": "^1.0.2",
+        "normalize-path": "^2.1.1"
+      },
+      "dependencies": {
+        "get-caller-file": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+          "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+          "dev": true
+        }
+      }
+    },
     "module-deps": {
       "version": "6.2.0",
       "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.0.tgz",
@@ -4249,9 +4151,9 @@
       }
     },
     "ms": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
-      "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
       "dev": true
     },
     "mute-stream": {
@@ -4290,12 +4192,6 @@
       "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==",
       "dev": true
     },
-    "nice-try": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
-      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
-      "dev": true
-    },
     "node-jsonrpc-request": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/node-jsonrpc-request/-/node-jsonrpc-request-1.0.0.tgz",
@@ -4318,21 +4214,15 @@
         "validate-npm-package-license": "^3.0.1"
       }
     },
-    "npm-run-path": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
-      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+    "normalize-path": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
       "dev": true,
       "requires": {
-        "path-key": "^2.0.0"
+        "remove-trailing-separator": "^1.0.1"
       }
     },
-    "number-is-nan": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-      "dev": true
-    },
     "nyc": {
       "version": "14.1.1",
       "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz",
@@ -4396,6 +4286,18 @@
       "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
       "dev": true
     },
+    "object.assign": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.2",
+        "function-bind": "^1.1.1",
+        "has-symbols": "^1.0.0",
+        "object-keys": "^1.0.11"
+      }
+    },
     "once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -4412,14 +4314,6 @@
       "dev": true,
       "requires": {
         "mimic-fn": "^1.0.0"
-      },
-      "dependencies": {
-        "mimic-fn": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
-          "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
-          "dev": true
-        }
       }
     },
     "optimist": {
@@ -4472,45 +4366,16 @@
       "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
       "dev": true
     },
-    "os-locale": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
-      "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
-      "dev": true,
-      "requires": {
-        "execa": "^1.0.0",
-        "lcid": "^2.0.0",
-        "mem": "^4.0.0"
-      }
-    },
     "os-tmpdir": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
       "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
       "dev": true
     },
-    "p-defer": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
-      "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
-      "dev": true
-    },
-    "p-finally": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
-      "dev": true
-    },
-    "p-is-promise": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
-      "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
-      "dev": true
-    },
     "p-limit": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
-      "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
+      "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
       "dev": true,
       "requires": {
         "p-try": "^2.0.0"
@@ -4624,12 +4489,6 @@
       "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
       "dev": true
     },
-    "path-key": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
-      "dev": true
-    },
     "path-parse": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
@@ -4861,16 +4720,6 @@
         "safe-buffer": "^5.1.2"
       }
     },
-    "pump": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
-      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
-      "dev": true,
-      "requires": {
-        "end-of-stream": "^1.1.0",
-        "once": "^1.3.1"
-      }
-    },
     "punycode": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
@@ -4886,155 +4735,29 @@
       }
     },
     "qrcode": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.3.tgz",
-      "integrity": "sha512-SH7V13AcJusH3GT8bMNOGz4w0L+LjcpNOU/NiOgtBhT/5DoWeZE6D5ntMJnJ84AMkoaM4kjJJoHoh9g++8lWFg==",
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.2.tgz",
+      "integrity": "sha512-eR6RgxFYPDFH+zFLTJKtoNP/RlsHANQb52AUmQ2bGDPMuUw7jJb0F+DNEgx7qQGIElrbFxWYMc0/B91zLZPF9Q==",
       "dev": true,
       "requires": {
-        "can-promise": "0.0.1",
         "dijkstrajs": "^1.0.1",
         "isarray": "^2.0.1",
         "pngjs": "^3.3.0",
-        "yargs": "^12.0.5"
+        "yargs": "^13.2.4"
       },
       "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
-        "cliui": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
-          "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
-          "dev": true,
-          "requires": {
-            "string-width": "^2.1.1",
-            "strip-ansi": "^4.0.0",
-            "wrap-ansi": "^2.0.0"
-          }
-        },
-        "get-caller-file": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
-          "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
-          "dev": true
-        },
         "isarray": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz",
-          "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==",
-          "dev": true
-        },
-        "require-main-filename": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
-          "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+          "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
           "dev": true
-        },
-        "string-width": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-          "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
-          "dev": true,
-          "requires": {
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^4.0.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        },
-        "wrap-ansi": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
-          "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
-          "dev": true,
-          "requires": {
-            "string-width": "^1.0.1",
-            "strip-ansi": "^3.0.1"
-          },
-          "dependencies": {
-            "ansi-regex": {
-              "version": "2.1.1",
-              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-              "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-              "dev": true
-            },
-            "is-fullwidth-code-point": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-              "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
-              "dev": true,
-              "requires": {
-                "number-is-nan": "^1.0.0"
-              }
-            },
-            "string-width": {
-              "version": "1.0.2",
-              "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-              "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-              "dev": true,
-              "requires": {
-                "code-point-at": "^1.0.0",
-                "is-fullwidth-code-point": "^1.0.0",
-                "strip-ansi": "^3.0.0"
-              }
-            },
-            "strip-ansi": {
-              "version": "3.0.1",
-              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-              "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-              "dev": true,
-              "requires": {
-                "ansi-regex": "^2.0.0"
-              }
-            }
-          }
-        },
-        "yargs": {
-          "version": "12.0.5",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
-          "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
-          "dev": true,
-          "requires": {
-            "cliui": "^4.0.0",
-            "decamelize": "^1.2.0",
-            "find-up": "^3.0.0",
-            "get-caller-file": "^1.0.1",
-            "os-locale": "^3.0.0",
-            "require-directory": "^2.1.1",
-            "require-main-filename": "^1.0.1",
-            "set-blocking": "^2.0.0",
-            "string-width": "^2.0.0",
-            "which-module": "^2.0.0",
-            "y18n": "^3.2.1 || ^4.0.0",
-            "yargs-parser": "^11.1.1"
-          }
-        },
-        "yargs-parser": {
-          "version": "11.1.1",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
-          "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
-          "dev": true,
-          "requires": {
-            "camelcase": "^5.0.0",
-            "decamelize": "^1.2.0"
-          }
         }
       }
     },
     "qs": {
-      "version": "6.7.0",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
-      "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
+      "version": "6.9.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.0.tgz",
+      "integrity": "sha512-27RP4UotQORTpmNQDX8BHPukOnBP3p1uUJY5UnDhaJB+rMt9iMsok724XL+UHU23bEFOHRMQ2ZhI99qOWUMGFA==",
       "dev": true
     },
     "querystring": {
@@ -5075,9 +4798,9 @@
       }
     },
     "react-is": {
-      "version": "16.8.6",
-      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
-      "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==",
+      "version": "16.11.0",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.11.0.tgz",
+      "integrity": "sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==",
       "dev": true
     },
     "read-only-stream": {
@@ -5134,6 +4857,12 @@
         "es6-error": "^4.0.1"
       }
     },
+    "remove-trailing-separator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+      "dev": true
+    },
     "repl.history": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/repl.history/-/repl.history-0.1.4.tgz",
@@ -5261,9 +4990,9 @@
       }
     },
     "rimraf": {
-      "version": "2.6.3",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
-      "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+      "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
       "dev": true,
       "requires": {
         "glob": "^7.1.3"
@@ -5329,15 +5058,15 @@
       }
     },
     "scryptsy": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.0.0.tgz",
-      "integrity": "sha1-Jiw28CMc+nZU4jY/o5TNLexm83g=",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz",
+      "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==",
       "dev": true
     },
     "secp256k1": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.7.0.tgz",
-      "integrity": "sha512-YlUIghD6ilkMkzmFJpIdVjiamv2S8lNZ9YMwm1XII9JC0NcR5qQiv2DOp/G37sExBtaMStzba4VDJtvBXEbmMQ==",
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.7.1.tgz",
+      "integrity": "sha512-1cf8sbnRreXrQFdH6qsg2H71Xw91fCCS9Yp021GnUNJzWJS/py96fS4lHbnTnouLp08Xj6jBoBB6V78Tdbdu5g==",
       "dev": true,
       "requires": {
         "bindings": "^1.5.0",
@@ -5346,14 +5075,14 @@
         "create-hash": "^1.2.0",
         "drbg.js": "^1.0.1",
         "elliptic": "^6.4.1",
-        "nan": "^2.13.2",
+        "nan": "^2.14.0",
         "safe-buffer": "^5.1.2"
       },
       "dependencies": {
         "elliptic": {
-          "version": "6.4.1",
-          "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
-          "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
+          "version": "6.5.1",
+          "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz",
+          "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==",
           "dev": true,
           "requires": {
             "bn.js": "^4.4.0",
@@ -5368,9 +5097,9 @@
       }
     },
     "semver": {
-      "version": "5.7.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
-      "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
       "dev": true
     },
     "set-blocking": {
@@ -5447,33 +5176,22 @@
       }
     },
     "slp-unit-test-data": {
-      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#4b0f5e773e3beea14a3385b2222b4d2239524b63",
+      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#22d9c5c95c3f1e1fb75958a82a211f4e99dcb9f1",
       "from": "git+https://github.com/simpleledger/slp-unit-test-data.git",
       "dev": true
     },
     "socket.io": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.2.0.tgz",
-      "integrity": "sha512-wxXrIuZ8AILcn+f1B4ez4hJTPG24iNgxBBDaJfT6MsyOhVYiTXWexGoPkd87ktJG8kQEcL/NBvRi64+9k4Kc0w==",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
+      "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
       "dev": true,
       "requires": {
         "debug": "~4.1.0",
-        "engine.io": "~3.3.1",
+        "engine.io": "~3.4.0",
         "has-binary2": "~1.0.2",
         "socket.io-adapter": "~1.1.0",
-        "socket.io-client": "2.2.0",
-        "socket.io-parser": "~3.3.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "4.1.1",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
-          "dev": true,
-          "requires": {
-            "ms": "^2.1.1"
-          }
-        }
+        "socket.io-client": "2.3.0",
+        "socket.io-parser": "~3.4.0"
       }
     },
     "socket.io-adapter": {
@@ -5483,17 +5201,17 @@
       "dev": true
     },
     "socket.io-client": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.2.0.tgz",
-      "integrity": "sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA==",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
+      "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
       "dev": true,
       "requires": {
         "backo2": "1.0.2",
         "base64-arraybuffer": "0.1.5",
         "component-bind": "1.0.0",
         "component-emitter": "1.2.1",
-        "debug": "~3.1.0",
-        "engine.io-client": "~3.3.1",
+        "debug": "~4.1.0",
+        "engine.io-client": "~3.4.0",
         "has-binary2": "~1.0.2",
         "has-cors": "1.1.0",
         "indexof": "0.0.1",
@@ -5504,54 +5222,58 @@
         "to-array": "0.1.4"
       },
       "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
+        "isarray": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+          "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+          "dev": true
         },
         "ms": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
           "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true
+        },
+        "socket.io-parser": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
+          "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
+          "dev": true,
+          "requires": {
+            "component-emitter": "1.2.1",
+            "debug": "~3.1.0",
+            "isarray": "2.0.1"
+          },
+          "dependencies": {
+            "debug": {
+              "version": "3.1.0",
+              "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+              "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+              "dev": true,
+              "requires": {
+                "ms": "2.0.0"
+              }
+            }
+          }
         }
       }
     },
     "socket.io-parser": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
-      "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz",
+      "integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==",
       "dev": true,
       "requires": {
         "component-emitter": "1.2.1",
-        "debug": "~3.1.0",
+        "debug": "~4.1.0",
         "isarray": "2.0.1"
       },
       "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
         "isarray": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
           "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
           "dev": true
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
         }
       }
     },
@@ -5562,9 +5284,9 @@
       "dev": true
     },
     "spawn-wrap": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz",
-      "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==",
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz",
+      "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==",
       "dev": true,
       "requires": {
         "foreground-child": "^1.5.6",
@@ -5602,9 +5324,9 @@
       }
     },
     "spdx-license-ids": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz",
-      "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==",
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+      "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
       "dev": true
     },
     "sprintf-js": {
@@ -5738,6 +5460,26 @@
         "function-bind": "^1.0.2"
       }
     },
+    "string.prototype.trimleft": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz",
+      "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "function-bind": "^1.1.1"
+      }
+    },
+    "string.prototype.trimright": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz",
+      "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "function-bind": "^1.1.1"
+      }
+    },
     "string_decoder": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -5762,12 +5504,6 @@
       "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
       "dev": true
     },
-    "strip-eof": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
-      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
-      "dev": true
-    },
     "strip-json-comments": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
@@ -5851,9 +5587,9 @@
       }
     },
     "tape": {
-      "version": "4.10.2",
-      "resolved": "https://registry.npmjs.org/tape/-/tape-4.10.2.tgz",
-      "integrity": "sha512-mgl23h7W2yuk3N85FOYrin2OvThTYWdwbk6XQ1pr2PMJieyW2FM/4Bu/+kD/wecb3aZ0Enm+Syinyq467OPq2w==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/tape/-/tape-4.11.0.tgz",
+      "integrity": "sha512-yixvDMX7q7JIs/omJSzSZrqulOV51EC9dK8dM0TzImTIkHWfe2/kFyL5v+d9C+SrCMaICk59ujsqFAVidDqDaA==",
       "dev": true,
       "requires": {
         "deep-equal": "~1.0.1",
@@ -5862,19 +5598,19 @@
         "function-bind": "~1.1.1",
         "glob": "~7.1.4",
         "has": "~1.0.3",
-        "inherits": "~2.0.3",
+        "inherits": "~2.0.4",
         "minimist": "~1.2.0",
         "object-inspect": "~1.6.0",
-        "resolve": "~1.10.1",
+        "resolve": "~1.11.1",
         "resumer": "~0.0.0",
         "string.prototype.trim": "~1.1.2",
         "through": "~2.3.8"
       },
       "dependencies": {
         "glob": {
-          "version": "7.1.4",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
-          "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+          "version": "7.1.5",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz",
+          "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==",
           "dev": true,
           "requires": {
             "fs.realpath": "^1.0.0",
@@ -5885,6 +5621,12 @@
             "path-is-absolute": "^1.0.0"
           }
         },
+        "inherits": {
+          "version": "2.0.4",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+          "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+          "dev": true
+        },
         "minimist": {
           "version": "1.2.0",
           "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
@@ -5892,9 +5634,9 @@
           "dev": true
         },
         "resolve": {
-          "version": "1.10.1",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz",
-          "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==",
+          "version": "1.11.1",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
+          "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==",
           "dev": true,
           "requires": {
             "path-parse": "^1.0.6"
@@ -5982,12 +5724,6 @@
         "punycode": "^1.4.1"
       }
     },
-    "trim-right": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
-      "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
-      "dev": true
-    },
     "ts-node": {
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz",
@@ -6070,6 +5806,23 @@
       "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==",
       "dev": true
     },
+    "typescript": {
+      "version": "3.6.4",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
+      "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
+      "dev": true
+    },
+    "typescript-tslint-plugin": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/typescript-tslint-plugin/-/typescript-tslint-plugin-0.5.4.tgz",
+      "integrity": "sha512-CQEfGC+p0SoBARI4N2LrGsWJsp4/OE+uKZ68xsWYKHWqMFq4DFQHqOVlK0deEricSN01NmDTqjap63Pw/DHieg==",
+      "dev": true,
+      "requires": {
+        "minimatch": "^3.0.4",
+        "mock-require": "^3.0.2",
+        "vscode-languageserver": "^5.1.0"
+      }
+    },
     "uglify-es": {
       "version": "3.3.9",
       "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
@@ -6120,9 +5873,9 @@
       "dev": true
     },
     "unorm": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.5.0.tgz",
-      "integrity": "sha512-sMfSWoiRaXXeDZSXC+YRZ23H4xchQpwxjpw1tmfR+kgbBCaOgln4NI0LXejJIhnBuKINrB3WRn+ZI8IWssirVw==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz",
+      "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==",
       "dev": true
     },
     "uri-js": {
@@ -6200,9 +5953,9 @@
       }
     },
     "varuint-bitcoin": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.0.tgz",
-      "integrity": "sha512-jCEPG+COU/1Rp84neKTyDJQr478/hAfVp5xxYn09QEH0yBjbmPeMfuuQIrp+BUD83hybtYZKhr5elV3bvdV1bA==",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz",
+      "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==",
       "dev": true,
       "requires": {
         "safe-buffer": "^5.1.1"
@@ -6225,6 +5978,44 @@
       "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==",
       "dev": true
     },
+    "vscode-jsonrpc": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz",
+      "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==",
+      "dev": true
+    },
+    "vscode-languageserver": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz",
+      "integrity": "sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A==",
+      "dev": true,
+      "requires": {
+        "vscode-languageserver-protocol": "3.14.1",
+        "vscode-uri": "^1.0.6"
+      }
+    },
+    "vscode-languageserver-protocol": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz",
+      "integrity": "sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==",
+      "dev": true,
+      "requires": {
+        "vscode-jsonrpc": "^4.0.0",
+        "vscode-languageserver-types": "3.14.0"
+      }
+    },
+    "vscode-languageserver-types": {
+      "version": "3.14.0",
+      "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz",
+      "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==",
+      "dev": true
+    },
+    "vscode-uri": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz",
+      "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ==",
+      "dev": true
+    },
     "which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -6249,12 +6040,6 @@
         "bs58check": "<3.0.0"
       }
     },
-    "window-or-global": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz",
-      "integrity": "sha1-2+RboqKRqrxW1iz2bEW3+jIpRt4=",
-      "dev": true
-    },
     "window-size": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz",
@@ -6305,12 +6090,12 @@
       }
     },
     "ws": {
-      "version": "6.1.4",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
-      "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz",
+      "integrity": "sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg==",
       "dev": true,
       "requires": {
-        "async-limiter": "~1.0.0"
+        "async-limiter": "^1.0.0"
       }
     },
     "xmlhttprequest-ssl": {
@@ -6338,28 +6123,27 @@
       "dev": true
     },
     "yargs": {
-      "version": "13.2.4",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz",
-      "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==",
+      "version": "13.3.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
+      "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
       "dev": true,
       "requires": {
         "cliui": "^5.0.0",
         "find-up": "^3.0.0",
         "get-caller-file": "^2.0.1",
-        "os-locale": "^3.1.0",
         "require-directory": "^2.1.1",
         "require-main-filename": "^2.0.0",
         "set-blocking": "^2.0.0",
         "string-width": "^3.0.0",
         "which-module": "^2.0.0",
         "y18n": "^4.0.0",
-        "yargs-parser": "^13.1.0"
+        "yargs-parser": "^13.1.1"
       }
     },
     "yargs-parser": {
-      "version": "13.1.0",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.0.tgz",
-      "integrity": "sha512-Yq+32PrijHRri0vVKQEm+ys8mbqWjLiwQkMFNXEENutzLPP0bE4Lcd4iA3OQY5HF+GD3xXxf0MEHb8E4/SA3AA==",
+      "version": "13.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
+      "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
       "dev": true,
       "requires": {
         "camelcase": "^5.0.0",

programming_language: JavaScript
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 {
   "name": "slpjs",
-  "version": "0.21.3",
+  "version": "0.21.4",
   "description": "Simple Ledger Protocol (SLP) JavaScript Library",
   "main": "index.js",
   "files": [
@@ -39,12 +39,14 @@
     "bitbox-sdk": "8.2.1",
     "bitcoin-rpc-promise": "^2.1.6",
     "browserify": "^16.2.2",
+    "grpc-bchrpc-node": "0.5.2",
     "mkdirp": "^0.5.1",
     "mocha": "^5.1.1",
     "slp-unit-test-data": "git+https://github.com/simpleledger/slp-unit-test-data.git",
     "ts-node": "^7.0.1",
     "uglify-es": "^3.3.9",
-    "grpc-bchrpc-node": "0.5.2"
+    "typescript-tslint-plugin": "^0.5.4",
+    "typescript": "^3.6.4"
   },
   "dependencies": {
     "@types/lodash": "^4.14.120",

programming_language: JavaScript
filename: readme.md
change_type: ModificationType.MODIFY
diff: @@ -772,6 +772,9 @@ Running the unit tests require node.js v8.15+.
 
 # Change Log
 
+### 0.21.4
+- Add ts linting / allow json comments
+
 ### 0.21.3
 - Judge NFT1 child created directly from valid NFT1 Parent Genesis as valid
 - Removed map files from npm package module

programming_language: Markdown
filename: tsconfig.json
change_type: ModificationType.MODIFY
diff: @@ -7,6 +7,9 @@
         "moduleResolution": "node",
         "lib":[ "es2017" ],
         "sourceMap": true, 
+        "plugins": [
+            { "name": "typescript-tslint-plugin" }
+        ]
     },
     "include": [
         "./lib",

programming_language: TypeScript
filename: tslint.json
change_type: ModificationType.ADD
diff: @@ -0,0 +1,8 @@
+// You can modify the configuration here with your own.
+// Make sure that your json is valid, and if you run into
+// other issues, try checking the console for errors.
+
+{
+    "extends": ["tslint:recommended"],
+    "rules": {} // add additional rules and their configuration
+}

programming_language: Markdown