CVE-2019-16762: Improper Input Validation (CWE-20)
value: 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.
CWE 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.
CVSS v3 base score: 6.1
Published: 2019-11-15T23:15Z
Repository: https://github.com/simpleledger/slpjs
Language: JSON
Files changed: .vscode/settings.json, lib/slp.ts, package-lock.json, package.json, readme.md, tsconfig.json, tslint.json
Commit date: 2019-10-28 08:42:15 -0400
Commit message: 0.21.4 updates:

* add ts linting / allow json comments

Diff: diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..3948809
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+    "files.associations": {
+        "*.json": "jsonc"
+    }
+}
\ No newline at end of file
diff --git a/lib/slp.ts b/lib/slp.ts
index 2f0b0eb..8fe7dd7 100644
--- a/lib/slp.ts
+++ b/lib/slp.ts
@@ -1,24 +1,26 @@
-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 {
     ticker: string|null;
     name: string|null;
@@ -28,41 +30,41 @@ export interface configBuildNFT1GenesisOpReturn {
 
 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 {
     tokenIdHex: string;
     batonVout: number|null; // normally this is null (for fixed supply) or 2+ for flexible
     mintQuantity: BigNumber;
 }
 
 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;
     batonReceiverSatoshis?: BigNumber;
     bchChangeReceiverAddress: string;
@@ -73,11 +75,11 @@ export interface configBuildRawGenesisTx {
 export interface configBuildRawSendTx {
     slpSendOpReturn: Buffer;
     input_token_utxos: utxo[];
     tokenReceiverAddressArray: string[];
     bchChangeReceiverAddress: string;
-    requiredNonTokenOutputs?: { satoshis: number, receiverAddress: string }[]
+    requiredNonTokenOutputs?: Array<{ satoshis: number, receiverAddress: string }>;
     extraFee?: number;
 }
 
 export interface configBuildBchSendTx {
     input_token_utxos: utxo[];
@@ -102,142 +104,202 @@ export interface configBuildRawBurnTx {
     input_token_utxos: utxo[];
     bchChangeReceiverAddress: string;
 }
 
 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[]>;
 }
 
 export interface SlpProxyValidator extends SlpValidator {
     validatorUrl: string;
 }
 
 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,
             config.documentUri,
             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)
         if (config.bchChangeReceiverAddress && bchChangeAfterFeeSatoshis.isGreaterThan(new BigNumber(546))) {
             config.bchChangeReceiverAddress = bchaddr.toCashAddress(config.bchChangeReceiverAddress);
@@ -245,108 +307,120 @@ 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)
                 .minus(config.extraFee ? config.extraFee : 0);
 
@@ -358,131 +432,146 @@ export class Slp {
         // Add dust dust outputs associated with tokens
 
         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
 
         if (bchChangeAfterFeeSatoshis.isGreaterThan(new BigNumber(546))) {
             config.bchChangeReceiverAddress = bchaddr.toCashAddress(config.bchChangeReceiverAddress);
             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++;
         }
 
         // Build the transaction to hex and return
         // 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)) {
             throw new Error("Mint receiver address not in SLP format.");
         }
         if (config.batonReceiverAddress && !bchaddr.isSlpAddress(config.batonReceiverAddress)) {
             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)
         if (config.bchChangeReceiverAddress && bchChangeAfterFeeSatoshis.isGreaterThan(new BigNumber(546))) {
             config.bchChangeReceiverAddress = bchaddr.toCashAddress(config.bchChangeReceiverAddress);
@@ -490,96 +579,111 @@ 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);
         }
 
         // Change
         if (bchChangeAfterFeeSatoshis.isGreaterThan(new BigNumber(546))) {
@@ -588,400 +692,426 @@ 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))) {
             config.bchChangeReceiverAddress = bchaddr.toCashAddress(config.bchChangeReceiverAddress);
             transactionBuilder.addOutput(config.bchChangeReceiverAddress, bchChangeAfterFeeSatoshis.toNumber());
         }
 
         // 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
             // # message, i.e., the number listed as `token_output_quantity1` in the
             // # 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;
     }
 
     private computeSlpBalances(utxos: SlpAddressUtxoResult[]) {
         const result: SlpBalancesResult = {
@@ -995,166 +1125,169 @@ export class Slp {
             nftParentChildBalances: {},
             slpTokenUtxos: {},
             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);
             }
         }
         return result;
     }
 
     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);
                 }
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/package-lock.json b/package-lock.json
index eca470c..8612755 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,31 +1,30 @@
 {
   "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": {
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz",
@@ -54,73 +53,62 @@
       "requires": {
         "@babel/types": "^7.4.4"
       }
     },
     "@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",
         "esutils": "^2.0.2",
         "js-tokens": "^4.0.0"
       }
     },
     "@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"
       }
     },
     "@protobufjs/aspromise": {
       "version": "1.1.2",
@@ -200,13 +188,13 @@
       "requires": {
         "@types/node": "*"
       }
     },
     "@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": {
       "version": "3.7.1",
       "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.7.1.tgz",
@@ -244,13 +232,13 @@
       "requires": {
         "@types/node": "*"
       }
     },
     "@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": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/@types/wif/-/wif-2.0.1.tgz",
@@ -389,11 +377,11 @@
         "default-require-extensions": "^2.0.0"
       }
     },
     "archy": {
       "version": "1.0.0",
-      "resolved": false,
+      "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
       "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
       "dev": true
     },
     "argparse": {
       "version": "1.0.10",
@@ -488,13 +476,13 @@
       "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
       "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
       "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": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -540,11 +528,11 @@
         "js-tokens": "^3.0.2"
       },
       "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
         },
         "ansi-styles": {
           "version": "2.2.1",
@@ -571,11 +559,11 @@
           "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
           "dev": true
         },
         "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": {
             "ansi-regex": "^2.0.0"
           }
@@ -619,13 +607,13 @@
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
       "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
       "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": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/bc-bip68/-/bc-bip68-1.0.5.tgz",
@@ -886,16 +874,17 @@
         "varuint-bitcoin": "^1.0.4",
         "wif": "^2.0.1"
       }
     },
     "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",
         "varuint-bitcoin": "^1.0.1"
       }
@@ -1230,19 +1219,10 @@
       "version": "5.3.1",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
       "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",
       "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
       "dev": true
@@ -1326,16 +1306,10 @@
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
       "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",
       "dev": true,
       "requires": {
@@ -1391,13 +1365,13 @@
       "requires": {
         "delayed-stream": "~1.0.0"
       }
     },
     "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
     },
     "commondir": {
       "version": "1.0.1",
@@ -1581,13 +1555,13 @@
       "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
       "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
       "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"
       }
     },
@@ -1796,90 +1770,58 @@
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
       "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",
         "parseuri": "0.0.5",
         "ws": "~6.1.0",
         "xmlhttprequest-ssl": "~1.5.4",
         "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",
         "arraybuffer.slice": "~0.0.7",
         "base64-arraybuffer": "0.1.5",
@@ -1895,21 +1837,25 @@
       "requires": {
         "is-arrayish": "^0.2.1"
       }
     },
     "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": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
@@ -1993,10 +1939,19 @@
             "lru-cache": "^4.0.1",
             "shebang-command": "^1.2.0",
             "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",
           "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
           "dev": true,
@@ -2044,13 +1999,13 @@
           "dev": true
         }
       }
     },
     "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",
         "pkg-dir": "^2.0.0"
       },
@@ -2314,13 +2269,13 @@
         "esrecurse": "^4.1.0",
         "estraverse": "^4.1.1"
       }
     },
     "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": {
       "version": "3.5.4",
       "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
@@ -2354,19 +2309,19 @@
       "requires": {
         "estraverse": "^4.1.0"
       }
     },
     "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": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz",
@@ -2381,40 +2336,10 @@
       "requires": {
         "md5.js": "^1.3.4",
         "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",
       "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
       "dev": true
@@ -2513,10 +2438,21 @@
       "requires": {
         "circular-json": "^0.3.1",
         "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": {
       "version": "1.5.10",
       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
@@ -2610,19 +2546,10 @@
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
       "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",
       "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
       "dev": true,
@@ -2655,13 +2582,13 @@
       "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.9.0.tgz",
       "integrity": "sha512-/t/7NvL9jAmfXLdqJG7Nv2c5Z2GTdZSKi/T6kI3hpIS4yA6rXqBAMF4Yx39xbICWDgRv+R+Z79n7ZxuvoUQU4w==",
       "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": {
       "version": "1.10.5",
       "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
@@ -3260,13 +3187,13 @@
           "dev": true
         }
       }
     },
     "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",
         "optimist": "^0.6.1",
         "source-map": "^0.6.1",
@@ -3278,17 +3205,17 @@
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
           "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
           "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"
           }
         }
       }
     },
@@ -3437,13 +3364,13 @@
         "minimalistic-assert": "^1.0.0",
         "minimalistic-crypto-utils": "^1.0.1"
       }
     },
     "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": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
@@ -3589,16 +3516,10 @@
         "through2": "^2.0.0",
         "undeclared-identifiers": "^1.1.2",
         "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",
       "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
       "dev": true
@@ -3716,13 +3637,13 @@
         "istanbul-lib-coverage": "^2.0.5",
         "semver": "^6.0.0"
       },
       "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
         }
       }
     },
     "istanbul-lib-report": {
@@ -3758,19 +3679,10 @@
         "make-dir": "^2.1.0",
         "rimraf": "^2.6.3",
         "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",
           "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
           "dev": true
@@ -3882,16 +3794,17 @@
         "json-schema": "0.2.3",
         "verror": "1.10.0"
       }
     },
     "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": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz",
@@ -3921,19 +3834,10 @@
           "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==",
           "dev": true
         }
       }
     },
-    "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",
       "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
       "dev": true,
@@ -4040,40 +3944,20 @@
       "version": "1.3.5",
       "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
       "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",
       "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
       "requires": {
         "hash-base": "^3.0.0",
         "inherits": "^2.0.1",
         "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",
       "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
       "dev": true,
@@ -4119,13 +4003,13 @@
       "requires": {
         "mime-db": "1.40.0"
       }
     },
     "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": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -4223,10 +4107,28 @@
             "has-flag": "^3.0.0"
           }
         }
       }
     },
+    "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",
       "integrity": "sha512-hKPmO06so6bL/ZvqVNVqdTVO8UAYsi3tQWlCa+z9KuWhoN4KDQtb5hcqQQv58qYiDE21wIvnttZEPiDgEbpwbA==",
       "dev": true,
@@ -4247,13 +4149,13 @@
         "through2": "^2.0.0",
         "xtend": "^4.0.0"
       }
     },
     "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": {
       "version": "0.0.7",
       "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
@@ -4288,16 +4190,10 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz",
       "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",
       "integrity": "sha512-3vNZG2CgorSYlWme4M7eIgF+xdHcTOYLQnj73/vvp47NKF3KP/IO4W5S4p0/NCKb6SWFWWky7LvJGNVQOc+Z7g==",
       "dev": true,
@@ -4316,25 +4212,19 @@
         "resolve": "^1.10.0",
         "semver": "2 || 3 || 4 || 5",
         "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",
       "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==",
       "dev": true,
@@ -4394,10 +4284,22 @@
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
       "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",
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
       "dev": true,
@@ -4410,18 +4312,10 @@
       "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
       "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
       "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": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
@@ -4470,49 +4364,20 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
       "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"
       }
     },
@@ -4622,16 +4487,10 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
       "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",
       "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
       "dev": true
@@ -4859,20 +4718,10 @@
         "parse-asn1": "^5.0.0",
         "randombytes": "^2.0.1",
         "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",
       "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
       "dev": true
@@ -4884,159 +4733,33 @@
       "requires": {
         "bitcoincash-ops": "github:Bitcoin-com/bitcoincash-ops#2.0.0"
       }
     },
     "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": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
@@ -5073,13 +4796,13 @@
         "randombytes": "^2.0.5",
         "safe-buffer": "^5.1.0"
       }
     },
     "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": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz",
@@ -5132,10 +4855,16 @@
       "dev": true,
       "requires": {
         "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",
       "integrity": "sha1-gDZxcfN4HW5CmccXWMJTCX9dWDI=",
       "dev": true
@@ -5259,13 +4988,13 @@
       "requires": {
         "through": "~2.3.4"
       }
     },
     "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"
       }
     },
@@ -5327,35 +5056,35 @@
       "requires": {
         "big.js": "^3.1.3"
       }
     },
     "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",
         "bip66": "^1.1.5",
         "bn.js": "^4.11.8",
         "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",
             "brorand": "^1.0.1",
             "hash.js": "^1.0.0",
@@ -5366,13 +5095,13 @@
           }
         }
       }
     },
     "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": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@@ -5445,128 +5174,121 @@
       "requires": {
         "is-fullwidth-code-point": "^2.0.0"
       }
     },
     "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": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
       "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=",
       "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",
         "object-component": "0.0.3",
         "parseqs": "0.0.5",
         "parseuri": "0.0.5",
         "socket.io-parser": "~3.3.0",
         "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
         }
       }
     },
     "source-map": {
       "version": "0.5.7",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
       "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
       "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",
         "mkdirp": "^0.5.0",
         "os-homedir": "^1.0.1",
@@ -5600,13 +5322,13 @@
         "spdx-exceptions": "^2.1.0",
         "spdx-license-ids": "^3.0.0"
       }
     },
     "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": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -5736,10 +5458,30 @@
         "define-properties": "^1.1.2",
         "es-abstract": "^1.5.0",
         "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",
       "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
       "dev": true,
@@ -5760,16 +5502,10 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
       "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",
       "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
       "dev": true
@@ -5849,54 +5585,60 @@
           }
         }
       }
     },
     "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",
         "defined": "~1.0.0",
         "for-each": "~0.3.3",
         "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",
             "inflight": "^1.0.4",
             "inherits": "2",
             "minimatch": "^3.0.4",
             "once": "^1.3.0",
             "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",
           "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
           "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"
           }
         }
@@ -5980,16 +5722,10 @@
       "requires": {
         "psl": "^1.1.24",
         "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",
       "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==",
       "dev": true,
@@ -6068,10 +5804,27 @@
       "version": "1.18.0",
       "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
       "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",
       "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
       "dev": true,
@@ -6118,13 +5871,13 @@
       "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
       "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
       "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": {
       "version": "4.2.2",
       "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
@@ -6198,13 +5951,13 @@
         "spdx-correct": "^3.0.0",
         "spdx-expression-parse": "^3.0.0"
       }
     },
     "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"
       }
     },
@@ -6223,10 +5976,48 @@
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz",
       "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",
       "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
       "dev": true,
@@ -6247,16 +6038,10 @@
       "dev": true,
       "requires": {
         "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",
       "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=",
       "dev": true
@@ -6303,16 +6088,16 @@
         "imurmurhash": "^0.1.4",
         "signal-exit": "^3.0.2"
       }
     },
     "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": {
       "version": "1.5.5",
       "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
@@ -6336,32 +6121,31 @@
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
       "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
       "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",
         "decamelize": "^1.2.0"
       }
diff --git a/package.json b/package.json
index 4bb7091..65a3848 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "name": "slpjs",
-  "version": "0.21.3",
+  "version": "0.21.4",
   "description": "Simple Ledger Protocol (SLP) JavaScript Library",
   "main": "index.js",
   "files": [
     "index.ts",
     "lib/*.js",
@@ -37,16 +37,18 @@
     "@types/mocha": "^5.1.1",
     "@types/node": "^10.12.18",
     "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",
     "axios": "^0.18.1",
     "bchaddrjs-slp": "git://github.com/simpleledger/bchaddrjs.git#master",
diff --git a/readme.md b/readme.md
index 9b17697..b878f82 100644
--- a/readme.md
+++ b/readme.md
@@ -770,10 +770,13 @@ 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
 
 ### 0.21.2
diff --git a/tsconfig.json b/tsconfig.json
index 3a97bfc..fcab3ab 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -5,10 +5,13 @@
         "target": "es5",
         "downlevelIteration": true,
         "moduleResolution": "node",
         "lib":[ "es2017" ],
         "sourceMap": true, 
+        "plugins": [
+            { "name": "typescript-tslint-plugin" }
+        ]
     },
     "include": [
         "./lib",
         "./test", 
         "./examples"
diff --git a/tslint.json b/tslint.json
new file mode 100644
index 0000000..f9c8687
--- /dev/null
+++ b/tslint.json
@@ -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
+}

Vulnerable code: import { SlpAddressUtxoResult, SlpTransactionDetails, SlpTransactionType, SlpUtxoJudgement, SlpBalancesResult, utxo, SlpVersionType, logger, Primatives } 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';
    address: string,
    amountBch?: number, 
    amountToken?: number,
    tokenId?: string,
    tokenFlags?: string[]
    opcode: number, 
    data: Buffer|null
    hash: Buffer|null,
    initialQuantity: BigNumber
    tokenIdHex: string; 
    outputQtyArray: BigNumber[]
    slpNFT1GenesisOpReturn: Buffer; 
    //batonReceiverAddress: string|null;
    //batonReceiverSatoshis?: BigNumber;
    //allowed_token_burning: string[]|null;
    slpGenesisOpReturn: Buffer; 
    requiredNonTokenOutputs?: { satoshis: number, receiverAddress: string }[]
    isValidSlpTxid(txid: string, tokenIdFilter?: string|null, tokenTypeFilter?: number|null, logger?: logger): Promise<boolean>;
    BITBOX: BITBOX;
    constructor(bitbox: BITBOX) {
        if(!bitbox)
            throw Error("Must provide BITBOX instance to class constructor.")
        this.BITBOX = bitbox;
    }
    get lokadIdHex() { return "534c5000" }
    static buildGenesisOpReturn(config: configBuildGenesisOpReturn, type = 0x01) {
        try { 
            hash = config.hash!.toString('hex')
        } catch (_) { hash = null }
        
            config.initialQuantity, 
            type
        )
    static buildMintOpReturn(config: configBuildMintOpReturn, type = 0x01) {
            config.mintQuantity, 
            type
        )
    static buildSendOpReturn(config: configBuildSendOpReturn, type = 0x01) {
            type
        )
    buildRawNFT1GenesisTx(config: configBuildRawNFT1GenesisTx, type = 0x01) {
        let config2: configBuildRawGenesisTx = {
            allowed_token_burning: [ config.parentTokenIdHex ]
        }
    buildRawGenesisTx(config: configBuildRawGenesisTx, type = 0x01) {
        if(config.mintReceiverSatoshis === undefined)
        if(config.batonReceiverSatoshis === undefined)
            config.batonReceiverSatoshis = new BigNumber(546); 
        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
            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.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 (!bchaddr.isSlpAddress(config.mintReceiverAddress))
        if (config.batonReceiverAddress && !bchaddr.isSlpAddress(config.batonReceiverAddress))
        let transactionBuilder = new this.BITBOX.TransactionBuilder(Utils.txnBuilderString(config.mintReceiverAddress));
        let genesisCost = this.calculateGenesisCost(config.slpGenesisOpReturn.length, config.input_utxos.length, config.batonReceiverAddress, config.bchChangeReceiverAddress);
        let bchChangeAfterFeeSatoshis: BigNumber = satoshis.minus(genesisCost);
        //bchChangeAfterFeeSatoshis -= config.mintReceiverSatoshis;
        let batonvout = this.parseSlpOutputScript(config.slpGenesisOpReturn).batonVout
            if(batonvout !== 2)
                throw Error("batonVout in transaction does not match OP_RETURN data.")
            //bchChangeAfterFeeSatoshis -= config.batonReceiverSatoshis;
            if(batonvout)
                throw Error("OP_RETURN has batonVout set to vout=" + batonvout + ", but a baton receiver address was not provided.")
            let paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
            transactionBuilder.sign(i, paymentKeyPair, undefined, transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
        let tx = transactionBuilder.build().toHex();
        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.")
    buildRawSendTx(config: configBuildRawSendTx, type = 0x01) {
        config.tokenReceiverAddressArray.forEach(outputAddress => {
            if (!bchaddr.isSlpAddress(outputAddress))
        if (!bchaddr.isSlpAddress(config.bchChangeReceiverAddress))
        
        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.")
                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.")
        })
        // Make sure the number of output receivers 
        let chgAddr = config.bchChangeReceiverAddress ? 1 : 0;
        if(!sendMsg.sendOutputs)
        if(config.tokenReceiverAddressArray.length + chgAddr !== sendMsg.sendOutputs.length)
            throw Error("Number of token receivers in config does not match the OP_RETURN 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.")
        let transactionBuilder = new this.BITBOX.TransactionBuilder(
        
        let inputSatoshis = config.input_token_utxos.reduce((t, i) => t.plus(i.satoshis), new BigNumber(0));
            token_utxo => transactionBuilder.addInput(token_utxo.txid, token_utxo.vout)); //, sequence);
        let bchOnlyCount = config.requiredNonTokenOutputs ? config.requiredNonTokenOutputs.length : 0;
        let bcOnlyOutputSatoshis = config.requiredNonTokenOutputs ? config.requiredNonTokenOutputs.reduce((t, v)=> t+=v.satoshis, 0): 0
        let sendCost = this.calculateSendCost(
                            config.slpSendOpReturn.length, 
                            config.input_token_utxos.length, 
                            config.tokenReceiverAddressArray.length + bchOnlyCount, 
        let bchChangeAfterFeeSatoshis = 
        })
        if(config.requiredNonTokenOutputs && config.requiredNonTokenOutputs.length > 0) {
                let outputAddress = bchaddr.toCashAddress(output.receiverAddress);
            })
        // Sign txn and add sig to p2pkh input for convenience if wif is provided, 
            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(!isComplete) {
            let tx = transactionBuilder.transaction.buildIncomplete();
        } 
        else
        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))
    buildRawMintTx(config: configBuildRawMintTx, type = 0x01) {
        let mintMsg = this.parseSlpOutputScript(config.slpMintOpReturn);
        if(config.mintReceiverSatoshis === undefined)
        if(config.batonReceiverSatoshis === undefined)
            config.batonReceiverSatoshis = new BigNumber(546); 
        if(config.batonReceiverAddress)
        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.")
        })
        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));
        let mintCost = this.calculateGenesisCost(config.slpMintOpReturn.length, config.input_baton_utxos.length, config.batonReceiverAddress, config.bchChangeReceiverAddress);
        let bchChangeAfterFeeSatoshis = satoshis.minus(mintCost);
        //bchChangeAfterFeeSatoshis -= config.mintReceiverSatoshis;
            if(this.parseSlpOutputScript(config.slpMintOpReturn).batonVout !== 2)
                throw Error("batonVout in transaction does not match OP_RETURN data.")
            //bchChangeAfterFeeSatoshis -= config.batonReceiverSatoshis;
            let paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
            transactionBuilder.sign(i, paymentKeyPair, undefined, transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
        let tx = transactionBuilder.build().toHex();
        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.")
    buildRawBurnTx(burnAmount: BigNumber, config: configBuildRawBurnTx, type = 0x01) {
        if(config.slpBurnOpReturn) {
            if(!sendMsg.sendOutputs)
    
            if(sendMsg.sendOutputs!.length !== 2)
            if(sendMsg.sendOutputs!.length === 2 && !config.bchChangeReceiverAddress)
            if (!bchaddr.isSlpAddress(config.bchChangeReceiverAddress))
        } else if(!config.tokenIdHex)
        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.")
                    if(txo.slpTransactionDetails.tokenIdHex !== config.tokenIdHex)
                        throw Error("Input UTXOs included a token for another tokenId.")
                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(config.slpBurnOpReturn) {
            //let chgAddr = config.bchChangeReceiverAddress ? 1 : 0;
            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.")
        let transactionBuilder = new this.BITBOX.TransactionBuilder(Utils.txnBuilderString(config.bchChangeReceiverAddress));
        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);
        if(config.slpBurnOpReturn) {
            let outputAddress = bchaddr.toCashAddress(config.bchChangeReceiverAddress);
            let paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
            transactionBuilder.sign(i, paymentKeyPair, undefined, transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
        let tx = transactionBuilder.build().toHex();
        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.")
    buildRawBchOnlyTx(config: configBuildBchSendTx) {
        config.bchReceiverAddressArray.forEach(outputAddress => {
            if (!bchaddr.isSlpAddress(outputAddress) && !bchaddr.isCashAddress(outputAddress))
        config.input_token_utxos.forEach(txo => {
            if(txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP)
                return
            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
            if(txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON)
            if(txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG || txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG)
        })
        let transactionBuilder = new this.BITBOX.TransactionBuilder(Utils.txnBuilderString(config.bchReceiverAddressArray[0]));
        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)));
        })
            let paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
            transactionBuilder.sign(i, paymentKeyPair, undefined, transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
        let tx = transactionBuilder.build().toHex();
        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.")
    parseSlpOutputScript(outputScript: Buffer): SlpTransactionDetails {
        let slpMsg = <SlpTransactionDetails>{};
        let chunks: (Buffer|null)[];
        } 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)
        if(!chunks[1])
            throw Error("Bad versionType buffer")
        slpMsg.versionType = <SlpVersionType>Slp.parseChunkToInt(chunks[1]!, 1, 2, true);
        let supportedTypes = [   
                SlpVersionType.TokenVersionType1, 
        if(!supportedTypes.includes(slpMsg.versionType))
            throw Error('Unsupported token type: ' + slpMsg.versionType);
        if(chunks.length === 2)
            throw Error('Missing SLP transaction type');
            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') : '';
            if(slpMsg.documentSha256) {
                if(slpMsg.documentSha256.length !== 0 && slpMsg.documentSha256.length !== 32)
            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')
            if(slpMsg.batonVout !== null) {
                if (slpMsg.batonVout < 2)
                    throw Error('Mint baton cannot be on vout=0 or 1');
            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');
            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.');
            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(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[5])
                throw Error("Bad Mint quantity buffer")
            if(chunks[5]!.length !== 8)
                throw Error("Mint quantity must be provided as an 8-byte buffer")
        }
        else
        if(!slpMsg.genesisOrMintQuantity && (!slpMsg.sendOutputs || slpMsg.sendOutputs.length === 0))
 
    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');
    }
    parseOpReturnToChunks(script: Buffer, allow_op_0=false, allow_op_number=false) {
    
        } catch(e) {
            //console.log(e);
            throw Error('Script error');
        if(ops[0].opcode !== this.BITBOX.Script.opcodes.OP_RETURN)
        let chunks: (Buffer|null)[] = [];
        ops.slice(1).forEach(opitem => {
            if(opitem.opcode > this.BITBOX.Script.opcodes.OP_16)
            if(opitem.opcode > this.BITBOX.Script.opcodes.OP_PUSHDATA4) {
                if(opitem.opcode === 80)
                if(!allow_op_number)
                if(opitem.opcode === this.BITBOX.Script.opcodes.OP_1NEGATE)
                else // OP_1 - OP_16
            if(opitem.opcode === this.BITBOX.Script.opcodes.OP_0 && !allow_op_0){
                throw Error('OP_0 not allowed');
            chunks.push(opitem.data)
        //console.log(chunks);
        return chunks
    getScriptOperations(script: Buffer) {
        let ops: PushDataOperation[] = [];
                let op: PushDataOperation = { opcode: script[n], data: null }
                if(op.opcode <= this.BITBOX.Script.opcodes.OP_PUSHDATA4) {
                    if(op.opcode < this.BITBOX.Script.opcodes.OP_PUSHDATA1)
                    else if(op.opcode === this.BITBOX.Script.opcodes.OP_PUSHDATA1) {
                    }
                    else if(op.opcode === this.BITBOX.Script.opcodes.OP_PUSHDATA2) {
                        dlen = script.slice(n, n + 2).readUIntLE(0,2);
                    }
                    else {
                        dlen = script.slice(n, n + 4).readUIntLE(0,4);
                    if((n + dlen) > script.length) {
                        throw Error('IndexError');
                    if(dlen > 0)
                    n += dlen
        } catch(e) {
            //console.log(e);
            throw Error('truncated script')
    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);
    calculateMintOrGenesisCost(mintOpReturnLength: number, inputUtxoSize: number, batonAddress: string|null, bchChangeAddress?: string, feeRate: number = 1) {
        let outputs = 1
        let nonfeeoutputs = 546
            nonfeeoutputs += 546
            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
    calculateSendCost(sendOpReturnLength: number, inputUtxoSize: number, outputAddressArraySize: number, bchChangeAddress?: string, feeRate = 1, forTokens=true) {
        if(forTokens)
            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
    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;
    }
    async processUtxosForSlpAbstract(utxos: SlpAddressUtxoResult[], asyncSlpValidator: SlpValidator) {
        
        for(let txo of utxos) {
            if(txo.slpUtxoJudgement === SlpUtxoJudgement.UNKNOWN || txo.slpUtxoJudgement === undefined)
                throw Error('Utxo SLP judgement has not been set, unknown error.');
    
        
    
        for(let id in result.slpTokenUtxos) tokenTxoCount += result.slpTokenUtxos[id].length;
        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.');
    
            invalidBatonUtxos: [], 
            unknownTokenTypeUtxos: []
                if (!(txo.slpTransactionDetails.tokenIdHex in result.slpTokenBalances))
                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.tokenIdHex in result.slpTokenUtxos))
                if(txo.slpTransactionDetails.versionType === SlpVersionType.TokenVersionType1_NFT_Child) {
                    if(!(txo.nftParentId! in result.nftParentChildBalances)) {
                    if(!(txo.slpTransactionDetails.tokenIdHex in result.nftParentChildBalances[txo.nftParentId!])) {
                        result.nftParentChildBalances[txo.nftParentId!][txo.slpTransactionDetails.tokenIdHex] = txo.slpUtxoJudgementAmount;
                        throw Error("Cannot have 2 UTXOs with the same NFT1 child token designation.")
            }
            else if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
                if(!(txo.slpTransactionDetails.tokenIdHex in result.slpBatonUtxos))
            }
            else if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG) {
            }
            else if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG) {
            }
            else if (txo.slpUtxoJudgement === SlpUtxoJudgement.UNSUPPORTED_TYPE) {
            }
            else {
            let vout = txo.tx.vout.find(vout => vout.n === 0);
            if (!vout)
            let vout0script = Buffer.from(vout.scriptPubKey.hex, 'hex');
            if (txo.slpTransactionDetails.transactionType === SlpTransactionType.GENESIS)
            // apply initial SLP judgement to the UTXO (based on OP_RETURN parsing ONLY! Still need to validate the DAG for possible tokens and batons!)
                }
                else if (txo.vout === 1 && txo.slpTransactionDetails.genesisOrMintQuantity!.isGreaterThan(0)) {
                    txo.slpUtxoJudgementAmount = <BigNumber>txo.slpTransactionDetails.genesisOrMintQuantity;
                }
                else
            }
            else if (txo.slpTransactionDetails.transactionType === SlpTransactionType.SEND && txo.slpTransactionDetails.sendOutputs) {
                }
                else
        }
        catch (e) {
            if(e.message.includes("Unsupported token type"))
                txo.slpUtxoJudgement = SlpUtxoJudgement.UNSUPPORTED_TYPE
            else
        let validSLPTx: string[] = await asyncSlpValidator.validateSlpTransactions([
            ...new Set(utxos.filter(txOut => {
                    txOut.slpUtxoJudgement !== SlpUtxoJudgement.NOT_SLP)
            }).map(txOut => txOut.txid))
                }
                else if (utxo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
        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));
            }
            else {
        }
        for(let txo in utxos) {
            if(utxos[txo].slpTransactionDetails && 
                if(utxos[txo].slpTransactionDetails.transactionType !== SlpTransactionType.GENESIS) {
}
  "version": "0.21.3",
      "version": "7.0.0",
      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
      "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
      "version": "7.4.4",
      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz",
      "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==",
        "@babel/types": "^7.4.4",
        "lodash": "^4.17.11",
        "source-map": "^0.5.0",
        "trim-right": "^1.0.1"
      "version": "7.0.0",
      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
      "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
      "version": "7.4.5",
      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz",
      "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==",
      "version": "7.4.4",
      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz",
      "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==",
        "@babel/parser": "^7.4.4",
        "@babel/types": "^7.4.4"
      "version": "7.4.5",
      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz",
      "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==",
        "@babel/code-frame": "^7.0.0",
        "@babel/generator": "^7.4.4",
        "@babel/parser": "^7.4.5",
        "@babel/types": "^7.4.4",
        "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"
          }
        }
      "version": "7.4.4",
      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz",
      "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==",
        "lodash": "^4.17.11",
      "version": "4.1.7",
      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.7.tgz",
      "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==",
      "version": "7.0.12",
      "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.0.12.tgz",
      "integrity": "sha512-fo0MWpVPSUrnZZhp9wyu+hhI3VJ9+Jhs+PWrokBTg3d2ryNPDOAWF1csIhQuYWBTn7KdZzXpRgpX2o6cwOlPWg==",
      "resolved": false,
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
          "resolved": false,
          "resolved": false,
      "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/bitcoinjs-message/-/bitcoinjs-message-2.0.0.tgz",
      "integrity": "sha512-H5pJC7/eSqVjREiEOZ4jifX+7zXYP3Y28GIOIqg9hrgE7Vj8Eva9+HnVqnxwA1rJPOwZKuw0vo6k0UxgVc6q1A==",
        "bs58check": "^2.0.2",
    "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"
      }
    },
    "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
    },
      "version": "2.20.0",
      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
      "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
      "version": "3.2.6",
      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
      "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
    "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"
      }
    },
      "version": "3.3.2",
      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.3.2.tgz",
      "integrity": "sha512-AsaA9KG7cWPXWHp5FvHdDWY3AMWeZ8x+2pUVLcn71qE5AtAzgGbxuclOytygskw8XGmiQafTmnI9Bix3uihu2w==",
        "base64id": "1.0.0",
        "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
        }
      "version": "3.3.2",
      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.3.2.tgz",
      "integrity": "sha512-y0CPINnhMvPuwtqXfsGuWE8BB66+B6wTtCofQDRecMQPYX3MYUZXFNKDhdrSe3EVjgOu4V3rxdeqN/Tr91IgbQ==",
        "debug": "~3.1.0",
        "engine.io-parser": "~2.1.1",
        "debug": {
          "version": "3.1.0",
          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
            "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
      "version": "2.1.3",
      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
      "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
      "version": "1.13.0",
      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
      "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
        "object-keys": "^1.0.12"
      "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": "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": "4.2.0",
      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
      "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
      "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
    "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"
          }
        }
      }
    },
    "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"
      }
    },
      "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.1.2",
      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
      "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
          "version": "3.5.15",
          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.15.tgz",
          "integrity": "sha512-fe7aYFotptIddkwcm6YuA0HmknBZ52ZzOsUxZEdhhkSsz7RfjHDX2QDxwKTiv4JQ5t5NhfmpgAK+J7LiDhKSqg==",
            "commander": "~2.20.0",
      "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==",
    "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
    },
          "version": "6.1.0",
          "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.0.tgz",
          "integrity": "sha512-kCqEOOHoBcFs/2Ccuk4Xarm/KiWRSLEX9CAZF8xkJ6ZPlIoTZ8V5f7J16vYLJqDbR7KrxTJpR2lqjIEm2Qx9cQ==",
        "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"
          }
        },
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.1.0.tgz",
      "integrity": "sha512-yDGDG2DS4JcqhA6blsuYbtsT09xL8AoLuUR2Gb5exrw7UEM19sBcOTq+YBBhrNbl0PUC4R4LnFu+dHg2HKeVvA==",
        "array-includes": "^3.0.3"
    "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"
      }
    },
    "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"
      }
    },
    "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"
      }
    },
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
      "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
    "nice-try": {
      "version": "1.0.5",
      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
      "dev": true
    },
    "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=",
        "path-key": "^2.0.0"
    "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
    },
      },
      "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
        }
    "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"
      }
    },
    "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
    },
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
      "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
    "path-key": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
      "dev": true
    },
    "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"
      }
    },
      "version": "1.3.3",
      "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.3.tgz",
      "integrity": "sha512-SH7V13AcJusH3GT8bMNOGz4w0L+LjcpNOU/NiOgtBhT/5DoWeZE6D5ntMJnJ84AMkoaM4kjJJoHoh9g++8lWFg==",
        "can-promise": "0.0.1",
        "yargs": "^12.0.5"
        "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
        },
          "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=",
        },
        "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"
          }
      "version": "6.7.0",
      "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
      "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
      "version": "16.8.6",
      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
      "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==",
      "version": "2.6.3",
      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
      "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.0.0.tgz",
      "integrity": "sha1-Jiw28CMc+nZU4jY/o5TNLexm83g=",
      "version": "3.7.0",
      "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.7.0.tgz",
      "integrity": "sha512-YlUIghD6ilkMkzmFJpIdVjiamv2S8lNZ9YMwm1XII9JC0NcR5qQiv2DOp/G37sExBtaMStzba4VDJtvBXEbmMQ==",
        "nan": "^2.13.2",
          "version": "6.4.1",
          "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
          "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
      "version": "5.7.0",
      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
      "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#4b0f5e773e3beea14a3385b2222b4d2239524b63",
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.2.0.tgz",
      "integrity": "sha512-wxXrIuZ8AILcn+f1B4ez4hJTPG24iNgxBBDaJfT6MsyOhVYiTXWexGoPkd87ktJG8kQEcL/NBvRi64+9k4Kc0w==",
        "engine.io": "~3.3.1",
        "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"
          }
        }
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.2.0.tgz",
      "integrity": "sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA==",
        "debug": "~3.1.0",
        "engine.io-client": "~3.3.1",
        "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"
          }
      "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==",
        "debug": "~3.1.0",
        "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
      "version": "1.4.2",
      "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz",
      "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==",
      "version": "3.0.4",
      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz",
      "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==",
    "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
    },
      "version": "4.10.2",
      "resolved": "https://registry.npmjs.org/tape/-/tape-4.10.2.tgz",
      "integrity": "sha512-mgl23h7W2yuk3N85FOYrin2OvThTYWdwbk6XQ1pr2PMJieyW2FM/4Bu/+kD/wecb3aZ0Enm+Syinyq467OPq2w==",
        "inherits": "~2.0.3",
        "resolve": "~1.10.1",
          "version": "7.1.4",
          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
          "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
          "version": "1.10.1",
          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz",
          "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==",
    "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
    },
      "version": "1.5.0",
      "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.5.0.tgz",
      "integrity": "sha512-sMfSWoiRaXXeDZSXC+YRZ23H4xchQpwxjpw1tmfR+kgbBCaOgln4NI0LXejJIhnBuKINrB3WRn+ZI8IWssirVw==",
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.0.tgz",
      "integrity": "sha512-jCEPG+COU/1Rp84neKTyDJQr478/hAfVp5xxYn09QEH0yBjbmPeMfuuQIrp+BUD83hybtYZKhr5elV3bvdV1bA==",
    "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
    },
      "version": "6.1.4",
      "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
      "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
        "async-limiter": "~1.0.0"
      "version": "13.2.4",
      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz",
      "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==",
        "os-locale": "^3.1.0",
        "yargs-parser": "^13.1.0"
      "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": "0.21.3",
    "grpc-bchrpc-node": "0.5.2"
Fixed code: {
    "files.associations": {
        "*.json": "jsonc"
    }
}
import {
    logger, Primatives, SlpAddressUtxoResult, SlpBalancesResult,
    SlpTransactionDetails, SlpTransactionType, SlpUtxoJudgement, SlpVersionType, utxo } from "../index";
import { SlpTokenType1 } from "./slptokentype1";
import { Utils } from "./utils";
import * as bchaddr from "bchaddrjs-slp";
import BigNumber from "bignumber.js";
import { BITBOX } from "bitbox-sdk";
    address: string;
    amountBch?: number;
    amountToken?: number;
    tokenId?: string;
    tokenFlags?: string[];
    opcode: number;
    data: Buffer|null;
    hash: Buffer|null;
    initialQuantity: BigNumber;
    tokenIdHex: string;
    outputQtyArray: BigNumber[];
    slpNFT1GenesisOpReturn: Buffer;
    // batonReceiverAddress: string|null;
    // batonReceiverSatoshis?: BigNumber;
    // allowed_token_burning: string[]|null;
    slpGenesisOpReturn: Buffer;
    requiredNonTokenOutputs?: Array<{ satoshis: number, receiverAddress: string }>;
    isValidSlpTxid(txid: string, tokenIdFilter?: string|null,
                   tokenTypeFilter?: number|null, logger?: logger): Promise<boolean>;
    get lokadIdHex() { return "534c5000"; }
    public static buildGenesisOpReturn(config: configBuildGenesisOpReturn, type = 0x01) {
        try {
            hash = config.hash!.toString("hex");
        } catch (_) { hash = null; }

            config.initialQuantity,
            type,
        );
    public static buildMintOpReturn(config: configBuildMintOpReturn, type = 0x01) {
            config.mintQuantity,
            type,
        );
    public static buildSendOpReturn(config: configBuildSendOpReturn, type = 0x01) {
            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;
    public buildRawNFT1GenesisTx(config: configBuildRawNFT1GenesisTx, type = 0x01) {
        const config2: configBuildRawGenesisTx = {
            allowed_token_burning: [ config.parentTokenIdHex ],
        };
    public buildRawGenesisTx(config: configBuildRawGenesisTx, type = 0x01) {
        if (config.mintReceiverSatoshis === undefined) {
        }
        if (config.batonReceiverSatoshis === undefined) {
            config.batonReceiverSatoshis = new BigNumber(546);
        } 
        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;
            } 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.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 (!bchaddr.isSlpAddress(config.mintReceiverAddress)) {
        }
        if (config.batonReceiverAddress && !bchaddr.isSlpAddress(config.batonReceiverAddress)) {
        }
        const transactionBuilder = new this.BITBOX.TransactionBuilder(
            Utils.txnBuilderString(config.mintReceiverAddress));
        const genesisCost = this.calculateGenesisCost(
            config.slpGenesisOpReturn.length,
            config.input_utxos.length,
            config.batonReceiverAddress,
            config.bchChangeReceiverAddress);
        const bchChangeAfterFeeSatoshis: BigNumber = satoshis.minus(genesisCost);
        // bchChangeAfterFeeSatoshis -= config.mintReceiverSatoshis;
        const batonvout = this.parseSlpOutputScript(config.slpGenesisOpReturn).batonVout;
            if (batonvout !== 2) {
                throw Error("batonVout in transaction does not match OP_RETURN data.");
            }
            // bchChangeAfterFeeSatoshis -= config.batonReceiverSatoshis;
            if (batonvout) {
                throw Error("OP_RETURN has batonVout set to vout=" + batonvout + ", but a baton receiver address was not provided.");
            }
            const paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
            transactionBuilder.sign(i, paymentKeyPair, undefined,
                transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
        const tx = transactionBuilder.build().toHex();
        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.");
        }
    public buildRawSendTx(config: configBuildRawSendTx, type = 0x01) {
        config.tokenReceiverAddressArray.forEach((outputAddress) => {
            if (!bchaddr.isSlpAddress(outputAddress)) {
            }
        if (!bchaddr.isSlpAddress(config.bchChangeReceiverAddress)) {
        }

        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.");
                }
                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.");
        });
        // Make sure the number of output receivers
        const chgAddr = config.bchChangeReceiverAddress ? 1 : 0;
        if (!sendMsg.sendOutputs) {
        }
        if (config.tokenReceiverAddressArray.length + chgAddr !== sendMsg.sendOutputs.length) {
            throw Error("Number of token receivers in config does not match the OP_RETURN 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.");
        }
        const transactionBuilder = new this.BITBOX.TransactionBuilder(

        const inputSatoshis = config.input_token_utxos.reduce((t, i) => t.plus(i.satoshis), new BigNumber(0));
            token_utxo => transactionBuilder.addInput(token_utxo.txid, token_utxo.vout)); // , sequence);
        const bchOnlyCount = config.requiredNonTokenOutputs ? config.requiredNonTokenOutputs.length : 0;
        const bcOnlyOutputSatoshis = config.requiredNonTokenOutputs ?
                                        config.requiredNonTokenOutputs.reduce((t, v) => t += v.satoshis, 0) : 0;
        const sendCost = this.calculateSendCost(
                            config.slpSendOpReturn.length,
                            config.input_token_utxos.length,
                            config.tokenReceiverAddressArray.length + bchOnlyCount,
        const bchChangeAfterFeeSatoshis =
        });
        if (config.requiredNonTokenOutputs && config.requiredNonTokenOutputs.length > 0) {
                const outputAddress = bchaddr.toCashAddress(output.receiverAddress);
            });
        // Sign txn and add sig to p2pkh input for convenience if wif is provided,
            if (txo.wif) {
                const paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
                transactionBuilder.sign(i, paymentKeyPair, undefined,
                    transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
            }
            else {
            }
        if (!isComplete) {
            const tx = transactionBuilder.transaction.buildIncomplete();
        } else {
        }
        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)) {
        }
    public buildRawMintTx(config: configBuildRawMintTx, type = 0x01) {
        const mintMsg = this.parseSlpOutputScript(config.slpMintOpReturn);
        if (config.mintReceiverSatoshis === undefined) {
        }
        if (config.batonReceiverSatoshis === undefined) {
            config.batonReceiverSatoshis = new BigNumber(546);
        }
        if (config.batonReceiverAddress) {
        }
        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.");
        });
        if (!config.input_baton_utxos.find((o) => o.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON)) {
            Error("There is no baton included with the input UTXOs.");
        }
        const transactionBuilder = new this.BITBOX.TransactionBuilder(
            Utils.txnBuilderString(config.mintReceiverAddress));
        const mintCost = this.calculateGenesisCost(config.slpMintOpReturn.length, config.input_baton_utxos.length,
            config.batonReceiverAddress, config.bchChangeReceiverAddress);
        const bchChangeAfterFeeSatoshis = satoshis.minus(mintCost);
        // bchChangeAfterFeeSatoshis -= config.mintReceiverSatoshis;
            if (this.parseSlpOutputScript(config.slpMintOpReturn).batonVout !== 2) {
                throw Error("batonVout in transaction does not match OP_RETURN data.");
            }
            // bchChangeAfterFeeSatoshis -= config.batonReceiverSatoshis;
            const paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
            transactionBuilder.sign(i, paymentKeyPair, undefined,
                transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
        const tx = transactionBuilder.build().toHex();
        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.");
        }
    public buildRawBurnTx(burnAmount: BigNumber, config: configBuildRawBurnTx, type = 0x01) {
        if (config.slpBurnOpReturn) {
            if (!sendMsg.sendOutputs) {
            }

            if (sendMsg.sendOutputs!.length !== 2) {
            }
            if (sendMsg.sendOutputs!.length === 2 && !config.bchChangeReceiverAddress) {
            }
            if (!bchaddr.isSlpAddress(config.bchChangeReceiverAddress)) {
            }
        } else if (!config.tokenIdHex) {
 }
        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.");
                    }
                    if (txo.slpTransactionDetails.tokenIdHex !== config.tokenIdHex) {
                        throw Error("Input UTXOs included a token for another tokenId.");
                    }
                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 (config.slpBurnOpReturn) {
            // let chgAddr = config.bchChangeReceiverAddress ? 1 : 0;
            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.");
            }
        const transactionBuilder = new this.BITBOX.TransactionBuilder(
            Utils.txnBuilderString(config.bchChangeReceiverAddress));
        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);
        if (config.slpBurnOpReturn) {
            const outputAddress = bchaddr.toCashAddress(config.bchChangeReceiverAddress);
            const paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
            transactionBuilder.sign(i, paymentKeyPair, undefined,
                transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
        const tx = transactionBuilder.build().toHex();
        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.");
        }
    public buildRawBchOnlyTx(config: configBuildBchSendTx) {
        config.bchReceiverAddressArray.forEach((outputAddress) => {
            if (!bchaddr.isSlpAddress(outputAddress) && !bchaddr.isCashAddress(outputAddress)) {
            }
        config.input_token_utxos.forEach((txo) => {
            if (txo.slpUtxoJudgement === SlpUtxoJudgement.NOT_SLP) {
                return;
            }
            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_TOKEN) {
            if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
            }
            if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG ||
                txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG) {
            }
        });
        const transactionBuilder = new this.BITBOX.TransactionBuilder(
            Utils.txnBuilderString(config.bchReceiverAddressArray[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)));
        });
            const paymentKeyPair = this.BITBOX.ECPair.fromWIF(txo.wif);
            transactionBuilder.sign(i, paymentKeyPair, undefined,
                transactionBuilder.hashTypes.SIGHASH_ALL, txo.satoshis.toNumber());
        const tx = transactionBuilder.build().toHex();
        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.");
        }
    public parseSlpOutputScript(outputScript: Buffer): SlpTransactionDetails {
        const slpMsg =  {} as SlpTransactionDetails;
        let chunks: Array<Buffer|null>;
        } 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) {
        }
        if (!chunks[1]) {
            throw Error("Bad versionType buffer");
        }
        slpMsg.versionType = ( Slp.parseChunkToInt(chunks[1]!, 1, 2, true) as SlpVersionType);
        const supportedTypes = [
                SlpVersionType.TokenVersionType1,
        if (!supportedTypes.includes(slpMsg.versionType)) {
            throw Error("Unsupported token type: " + slpMsg.versionType);
        }
        if (chunks.length === 2) {
            throw Error("Missing SLP transaction type");
        }
            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") : "";
            if (slpMsg.documentSha256) {
                if (slpMsg.documentSha256.length !== 0 && slpMsg.documentSha256.length !== 32) {
                }
            }
            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 (slpMsg.batonVout !== null) {
                if (slpMsg.batonVout < 2) {
                    throw Error("Mint baton cannot be on vout=0 or 1");
                }
            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");
            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.");
                }
            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 (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[5]) {
                throw Error("Bad Mint quantity buffer");
            }
            if (chunks[5]!.length !== 8) {
                throw Error("Mint quantity must be provided as an 8-byte buffer");
            }
        } else {
        }
        if (!slpMsg.genesisOrMintQuantity && (!slpMsg.sendOutputs || slpMsg.sendOutputs.length === 0)) {
        }
    public parseOpReturnToChunks(script: Buffer, allow_op_0= false, allow_op_number= false) {

        } catch (e) {
            // console.log(e);
            throw Error("Script error");
        if (ops[0].opcode !== this.BITBOX.Script.opcodes.OP_RETURN) {
        }
        const chunks: Array<Buffer|null> = [];
        ops.slice(1).forEach((opitem) => {
            if (opitem.opcode > this.BITBOX.Script.opcodes.OP_16) {
            }
            if (opitem.opcode > this.BITBOX.Script.opcodes.OP_PUSHDATA4) {
                if (opitem.opcode === 80) {
                }
                if (!allow_op_number) {
                }
                if (opitem.opcode === this.BITBOX.Script.opcodes.OP_1NEGATE) {
                }
                else { // OP_1 - OP_16
                }
            if (opitem.opcode === this.BITBOX.Script.opcodes.OP_0 && !allow_op_0) {
                throw Error("OP_0 not allowed");
            chunks.push(opitem.data);
        // console.log(chunks);
        return chunks;
    public getScriptOperations(script: Buffer) {
        const ops: PushDataOperation[] = [];
                const op: PushDataOperation = { opcode: script[n], data: null };
                if (op.opcode <= this.BITBOX.Script.opcodes.OP_PUSHDATA4) {
                    if (op.opcode < this.BITBOX.Script.opcodes.OP_PUSHDATA1) {
                    }
                    else if (op.opcode === this.BITBOX.Script.opcodes.OP_PUSHDATA1) {
                    } else if (op.opcode === this.BITBOX.Script.opcodes.OP_PUSHDATA2) {
                        dlen = script.slice(n, n + 2).readUIntLE(0, 2);
                    } else {
                        dlen = script.slice(n, n + 4).readUIntLE(0, 4);
                    if ((n + dlen) > script.length) {
                        throw Error("IndexError");
                    if (dlen > 0) {
                    }
                    n += dlen;
        } catch (e) {
            // console.log(e);
            throw Error("truncated script");
    public calculateGenesisCost(genesisOpReturnLength: number, inputUtxoSize: number,
                                batonAddress: string | null, bchChangeAddress?: string, feeRate = 1) {
        return this.calculateMintOrGenesisCost(genesisOpReturnLength,
            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);
    public calculateMintOrGenesisCost(mintOpReturnLength: number, inputUtxoSize: number,
                                      batonAddress: string|null, bchChangeAddress?: string, feeRate: number = 1) {
        let outputs = 1;
        let nonfeeoutputs = 546;
            nonfeeoutputs += 546;
            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;
    public calculateSendCost(sendOpReturnLength: number, inputUtxoSize: number, 
                             outputAddressArraySize: number, bchChangeAddress?: string, feeRate = 1, forTokens= true) {
        if (forTokens) {
        }
            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;
    public async processUtxosForSlpAbstract(utxos: SlpAddressUtxoResult[], asyncSlpValidator: SlpValidator) {
        for (const txo of utxos) {
            if (txo.slpUtxoJudgement === SlpUtxoJudgement.UNKNOWN || txo.slpUtxoJudgement === undefined) {
                throw Error("Utxo SLP judgement has not been set, unknown error.");
            }



        for (const id in result.slpTokenUtxos) { tokenTxoCount += result.slpTokenUtxos[id].length; }
        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.");
        }

            invalidBatonUtxos: [],
            unknownTokenTypeUtxos: [],
                if (!(txo.slpTransactionDetails.tokenIdHex in result.slpTokenBalances)) {
                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.");
                if (!(txo.slpTransactionDetails.tokenIdHex in result.slpTokenUtxos)) {
                }
                if (txo.slpTransactionDetails.versionType === SlpVersionType.TokenVersionType1_NFT_Child) {
                    if (!(txo.nftParentId! in result.nftParentChildBalances)) {
                    if (!(txo.slpTransactionDetails.tokenIdHex in result.nftParentChildBalances[txo.nftParentId!])) {
                        result.nftParentChildBalances[txo.nftParentId!][txo.slpTransactionDetails.tokenIdHex] =
                        txo.slpUtxoJudgementAmount;
                        throw Error("Cannot have 2 UTXOs with the same NFT1 child token designation.");
            } else if (txo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
                if (!(txo.slpTransactionDetails.tokenIdHex in result.slpBatonUtxos)) {
                }
            } else if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_TOKEN_DAG) {
            } else if (txo.slpUtxoJudgement === SlpUtxoJudgement.INVALID_BATON_DAG) {
            } else if (txo.slpUtxoJudgement === SlpUtxoJudgement.UNSUPPORTED_TYPE) {
            } else {
            const vout = txo.tx.vout.find(vout => vout.n === 0);
            if (!vout) {
            }
            const vout0script = Buffer.from(vout.scriptPubKey.hex, "hex");
            if (txo.slpTransactionDetails.transactionType === SlpTransactionType.GENESIS) {
            }
            // apply initial SLP judgement to the UTXO (based on OP_RETURN 
            // parsing ONLY! Still need to validate the DAG for possible tokens and batons!)
                } else if (txo.vout === 1 && txo.slpTransactionDetails.genesisOrMintQuantity!.isGreaterThan(0)) {
                    txo.slpUtxoJudgementAmount = ( txo.slpTransactionDetails.genesisOrMintQuantity as BigNumber);
                } else {
 }
            } else if (txo.slpTransactionDetails.transactionType === SlpTransactionType.SEND &&
                txo.slpTransactionDetails.sendOutputs) {
                } else {
                }
        } catch (e) {
            if (e.message.includes("Unsupported token type")) {
                txo.slpUtxoJudgement = SlpUtxoJudgement.UNSUPPORTED_TYPE;
            } else {
            }
        const validSLPTx: string[] = await asyncSlpValidator.validateSlpTransactions([
            ...new Set(utxos.filter((txOut) => {
                    txOut.slpUtxoJudgement !== SlpUtxoJudgement.NOT_SLP) {
                }
            }).map((txOut) => txOut.txid)),
                } else if (utxo.slpUtxoJudgement === SlpUtxoJudgement.SLP_BATON) {
        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));
            } else {
        };
        for (const txo in utxos) {
            if (utxos[txo].slpTransactionDetails &&
                if (utxos[txo].slpTransactionDetails.transactionType !== SlpTransactionType.GENESIS) {
}
  "version": "0.21.4",
      "version": "7.5.5",
      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
      "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
      "version": "7.6.4",
      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz",
      "integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==",
        "@babel/types": "^7.6.3",
        "lodash": "^4.17.13",
        "source-map": "^0.5.0"
      "version": "7.5.0",
      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
      "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
      "version": "7.6.4",
      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz",
      "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==",
      "version": "7.6.0",
      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz",
      "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==",
        "@babel/parser": "^7.6.0",
        "@babel/types": "^7.6.0"
      "version": "7.6.3",
      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz",
      "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==",
        "@babel/code-frame": "^7.5.5",
        "@babel/generator": "^7.6.3",
        "@babel/parser": "^7.6.3",
        "@babel/types": "^7.6.3",
        "lodash": "^4.17.13"
      "version": "7.6.3",
      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz",
      "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==",
        "lodash": "^4.17.13",
      "version": "4.2.4",
      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.4.tgz",
      "integrity": "sha512-7qvf9F9tMTzo0akeswHPGqgUx/gIaJqrOEET/FCD8CFRkSUHlygQiM5yB6OvjrtdxBVLSyw7COJubsFYs0683g==",
      "version": "7.5.0",
      "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.0.tgz",
      "integrity": "sha512-NyzhuSBy97B/zE58cDw4NyGvByQbAHNP9069KVSgnXt/sc0T6MFRh0InKAeBVHJWdSXG1S3+PxgVIgKo9mTHbw==",
      "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
      "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.1.0.tgz",
      "integrity": "sha512-xVL2YvyAJUI8ZwfNpi6Ju5zda3+QYGHTLUvISDb0VHWbsWn9Zyvd1o8XHRC/0r+DNwDIwenpXDSPl1XLCMGnMA==",
        "bech32": "^1.1.3",
        "bs58check": "^2.1.2",
      "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==",
      "version": "4.1.1",
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
      "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
      "version": "3.4.0",
      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.0.tgz",
      "integrity": "sha512-XCyYVWzcHnK5cMz7G4VTu2W7zJS7SM1QkcelghyIk/FmobWBtXE7fwhBusEKvCSqc3bMh8fNFMlUkCKTFRxH2w==",
        "base64id": "2.0.0",
        "debug": "~4.1.0",
        "engine.io-parser": "~2.2.0",
        "ws": "^7.1.2"
      "version": "3.4.0",
      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz",
      "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==",
        "debug": "~4.1.0",
        "engine.io-parser": "~2.2.0",
        "ws": {
          "version": "6.1.4",
          "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
          "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
            "async-limiter": "~1.0.0"
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
      "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
      "version": "1.16.0",
      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz",
      "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==",
        "has-symbols": "^1.0.0",
        "object-inspect": "^1.6.0",
        "object-keys": "^1.1.1",
        "string.prototype.trimleft": "^2.1.0",
        "string.prototype.trimright": "^2.1.0"
        "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"
          }
        },
      "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==",
      "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==",
      "version": "4.3.0",
      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
      "version": "2.0.3",
      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
      },
      "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"
          }
        }
      "version": "4.2.3",
      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
      "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
      "version": "4.4.5",
      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.5.tgz",
      "integrity": "sha512-0Ce31oWVB7YidkaTq33ZxEbN+UDxMMgThvCe8ptgQViymL5DPis9uLdTA13MiRPhgvqyxIegugrP97iK3JeBHg==",
          "version": "3.6.4",
          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.4.tgz",
          "integrity": "sha512-9Yc2i881pF4BPGhjteCXQNaXx1DCwm3dtOyBaG2hitHjLWOczw/ki8vD1bqyT3u6K0Ms/FpCShkmfg+FtlOfYA==",
            "commander": "~2.20.3",
      "version": "2.8.5",
      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz",
      "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==",
          "version": "6.3.0",
          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
      "version": "2.2.3",
      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz",
      "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==",
        "array-includes": "^3.0.3",
        "object.assign": "^4.1.0"
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
      "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
    "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
        }
      }
    },
      "version": "2.1.2",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
    "normalize-path": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
        "remove-trailing-separator": "^1.0.1"
    "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"
      }
    },
      "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==",
      "version": "1.4.2",
      "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.2.tgz",
      "integrity": "sha512-eR6RgxFYPDFH+zFLTJKtoNP/RlsHANQb52AUmQ2bGDPMuUw7jJb0F+DNEgx7qQGIElrbFxWYMc0/B91zLZPF9Q==",
        "yargs": "^13.2.4"
          "version": "2.0.5",
          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
          "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
      "version": "6.9.0",
      "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.0.tgz",
      "integrity": "sha512-27RP4UotQORTpmNQDX8BHPukOnBP3p1uUJY5UnDhaJB+rMt9iMsok724XL+UHU23bEFOHRMQ2ZhI99qOWUMGFA==",
      "version": "16.11.0",
      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.11.0.tgz",
      "integrity": "sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==",
    "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
    },
      "version": "2.7.1",
      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
      "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz",
      "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==",
      "version": "3.7.1",
      "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.7.1.tgz",
      "integrity": "sha512-1cf8sbnRreXrQFdH6qsg2H71Xw91fCCS9Yp021GnUNJzWJS/py96fS4lHbnTnouLp08Xj6jBoBB6V78Tdbdu5g==",
        "nan": "^2.14.0",
          "version": "6.5.1",
          "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz",
          "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==",
      "version": "5.7.1",
      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#22d9c5c95c3f1e1fb75958a82a211f4e99dcb9f1",
      "version": "2.3.0",
      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
      "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
        "engine.io": "~3.4.0",
        "socket.io-client": "2.3.0",
        "socket.io-parser": "~3.4.0"
      "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==",
        "debug": "~4.1.0",
        "engine.io-client": "~3.4.0",
        "isarray": {
          "version": "2.0.1",
          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
          "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
          "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"
              }
            }
          }
      "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==",
        "debug": "~4.1.0",
      "version": "1.4.3",
      "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz",
      "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==",
      "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==",
    "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"
      }
    },
      "version": "4.11.0",
      "resolved": "https://registry.npmjs.org/tape/-/tape-4.11.0.tgz",
      "integrity": "sha512-yixvDMX7q7JIs/omJSzSZrqulOV51EC9dK8dM0TzImTIkHWfe2/kFyL5v+d9C+SrCMaICk59ujsqFAVidDqDaA==",
        "inherits": "~2.0.4",
        "resolve": "~1.11.1",
          "version": "7.1.5",
          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz",
          "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==",
        "inherits": {
          "version": "2.0.4",
          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
          "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
          "dev": true
        },
          "version": "1.11.1",
          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
          "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==",
    "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"
      }
    },
      "version": "1.6.0",
      "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz",
      "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==",
      "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==",
    "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
    },
      "version": "7.2.0",
      "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz",
      "integrity": "sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg==",
        "async-limiter": "^1.0.0"
      "version": "13.3.0",
      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
      "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
        "yargs-parser": "^13.1.1"
      "version": "13.1.1",
      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
      "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
  "version": "0.21.4",
    "grpc-bchrpc-node": "0.5.2",
    "typescript-tslint-plugin": "^0.5.4",
    "typescript": "^3.6.4"
### 0.21.4
- Add ts linting / allow json comments

        "plugins": [
            { "name": "typescript-tslint-plugin" }
        ]
// 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
}