CVE-2019-16762 (JSON, published 2019-11-15T23:15Z)
Description: 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.
Severity: MEDIUM (CVSS v3 base score: 6.1)
Weakness: CWE-20 \u2014 Improper Input Validation
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.
Repository: https://github.com/simpleledger/slpjs
Files changed:
.vscode/settings.json
lib/slp.ts
package-lock.json
package.json
readme.md
tsconfig.json
tslint.json
Commit message: 0.21.4 updates:

* add ts linting / allow json comments


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
}