diff --git a/README.md b/README.md index 1c4d866..45ba3c2 100644 --- a/README.md +++ b/README.md @@ -36,11 +36,10 @@ npm install --save crowdnode@v1 ## CrowdNode Browser SDK ```html - - + + - ``` @@ -124,7 +123,7 @@ APIs, but which you could learn from in [crowdnode-cli](/bin/crowdnode.js). ```js CrowdNode.offset = 20000; -CrowdNode.duffs = 100000000; +CrowdNode.satoshis = 100000000; CrowdNode.depositMinimum = 100000; CrowdNode.requests = { @@ -188,8 +187,8 @@ await CrowdNode.accept(wif, hotwallet); * } */ -// amount given in DUFFs -await CrowdNode.deposit(wif, hotwallet, (amount = 0)); +// satoshis (a.k.a. duffs) is the base unit of DASH +await CrowdNode.deposit(wif, hotwallet, (satoshis = 0)); /** @type SocketPayment * { * "address": "Xj00000000000000000000000000000000", @@ -282,7 +281,7 @@ await CrowdNode.http.VotingOpen(pub); | Term | Description | | ------------- | ------------------------------------------------------------- | | addr | your Dash address (Base58Check-encoded Pay-to-PubKey Address) | -| amount | the integer value of "Duffs" (Đ/100000000) | +| satoshis | the base unit of DASH (a.k.a. "Duffs") Đ/100000000 | | permil | 1/1000, 1‰, or 0.1% - between 1 and 1000 (0.1% to 100.0%) | | ./privkey.wif | the file path to your staking key in WIF (Base58Check) format | diff --git a/bin/_qr-node.js b/bin/_qr-node.js index 4d374a1..d614616 100644 --- a/bin/_qr-node.js +++ b/bin/_qr-node.js @@ -16,3 +16,5 @@ async function save(filepath, data, opts) { //@ts-ignore Qr.save = save; + +module.exports = Qr; diff --git a/bin/crowdnode.js b/bin/crowdnode.js index 8bd792c..341f140 100755 --- a/bin/crowdnode.js +++ b/bin/crowdnode.js @@ -16,24 +16,24 @@ let Path = require("path"); let Cipher = require("./_cipher.js"); let CrowdNode = require("../crowdnode.js"); let Dash = require("../dashapi.js"); -let Insight = require("dashsight"); +let Dashsight = require("dashsight"); let Prompt = require("./_prompt.js"); let Qr = require("./_qr-node.js"); let Ws = require("dashsight/ws"); -let Dashcore = require("@dashevo/dashcore-lib"); +let Dashcore = require("../dashcore-lit.js"); const DONE = "✅"; const TODO = "ℹ️"; const NO_SHADOW = "NONE"; -const DUFFS = 100000000; +const SATOSHIS = 100000000; let shownDefault = false; let qrWidth = 2 + 33 + 2; // Sign Up Fees: // 0.00236608 // required for signup // 0.00002000 // TX fee estimate -// 0.00238608 // minimum recommended amount +// 0.00238608 // minimum recommended DASH // Target: // 0.01000000 let signupOnly = CrowdNode.requests.signupForApi + CrowdNode.requests.offset; @@ -137,6 +137,14 @@ function showHelp() { let cmds = {}; +let dashsightBaseUrl = + process.env.DASHSIGHT_BASE_URL || + "https://dashsight.dashincubator.dev/insight-api"; +let dashsocketBaseUrl = + process.env.DASHSOCKET_BASE_URL || "https://insight.dash.org/socket.io"; +let insightBaseUrl = + process.env.INSIGHT_BASE_URL || "https://insight.dash.org/insight-api"; + async function main() { /*jshint maxcomplexity:40 */ /*jshint maxstatements:500 */ @@ -180,17 +188,18 @@ async function main() { ); defaultAddr = defaultAddr.trim(); - let insightBaseUrl = - process.env.INSIGHT_BASE_URL || "https://insight.dash.org"; - let insightApi = Insight.create({ baseUrl: insightBaseUrl }); - let dashApi = Dash.create({ insightApi: insightApi }); + let dashsightApi = Dashsight.create({ + dashsightBaseUrl: dashsightBaseUrl, + dashsocketBaseUrl: dashsocketBaseUrl, + insightBaseUrl: insightBaseUrl, + }); + let dashApi = Dash.create({ insightApi: dashsightApi }); if ("stake" === subcommand) { await stakeDash( { dashApi, - insightApi, - insightBaseUrl, + insightApi: dashsightApi, defaultAddr, forceGenerate, noReserve, @@ -290,7 +299,12 @@ async function main() { // helper for debugging if ("transfer" === subcommand) { await transferBalance( - { dashApi, defaultAddr, forceConfirm, insightBaseUrl, insightApi }, + { + dashApi, + defaultAddr, + forceConfirm, + insightApi: dashsightApi, + }, args, ); process.exit(0); @@ -308,7 +322,7 @@ async function main() { let [addr] = await mustGetAddr({ defaultAddr }, args); - await initCrowdNode(insightBaseUrl); + await initCrowdNode(); // ex: http (, ...) args.unshift(addr); let hasRpc = rpc in CrowdNode.http; @@ -339,7 +353,7 @@ async function main() { // keeping rm for backwards compat if ("rm" === subcommand || "delete" === subcommand) { - await initCrowdNode(insightBaseUrl); + await initCrowdNode(); let [addr, filepath] = await mustGetAddr({ defaultAddr }, args); await removeKey({ addr, dashApi, filepath, insightBaseUrl }, args); process.exit(0); @@ -377,10 +391,7 @@ async function main() { } if ("deposit" === subcommand) { - await depositDash( - { dashApi, defaultAddr, insightBaseUrl, noReserve }, - args, - ); + await depositDash({ dashApi, defaultAddr, noReserve }, args); process.exit(0); return; } @@ -416,14 +427,7 @@ async function main() { * @param {Array} args */ async function stakeDash( - { - dashApi, - defaultAddr, - forceGenerate, - insightApi, - insightBaseUrl, - noReserve, - }, + { dashApi, defaultAddr, forceGenerate, insightApi, noReserve }, args, ) { let err = await Fs.access(args[0]).catch(Object); @@ -446,6 +450,8 @@ async function stakeDash( console.info("Checking CrowdNode account... "); await CrowdNode.init({ baseUrl: "https://app.crowdnode.io", + dashsightBaseUrl, + dashsocketBaseUrl, insightBaseUrl, }); let hotwallet = CrowdNode.main.hotwallet; @@ -466,21 +472,20 @@ async function stakeDash( extra += acceptDeposit; } - let desiredAmountDash = args.shift() || "0.5"; - let effectiveDuff = toDuff(desiredAmountDash); - effectiveDuff += extra; + let desiredDashAmount = args.shift() || "0.5"; + let effectiveSats = toDuff(desiredDashAmount); + effectiveSats += extra; let balanceInfo = await dashApi.getInstantBalance(addr); - effectiveDuff -= balanceInfo.balanceSat; + effectiveSats -= balanceInfo.balanceSat; - if (effectiveDuff > 0) { - effectiveDuff = roundDuff(effectiveDuff, 3); - let effectiveDash = toDash(effectiveDuff); + if (effectiveSats > 0) { + effectiveSats = roundDuff(effectiveSats, 3); + let effectiveDash = toDash(effectiveSats); await plainLoadAddr({ addr, effectiveDash, - effectiveDuff, - insightBaseUrl, + effectiveSats, }); } @@ -492,7 +497,7 @@ async function stakeDash( } await depositDash( - { dashApi, defaultAddr: addr, insightBaseUrl, noReserve }, + { dashApi, defaultAddr: addr, noReserve }, [addr].concat(args), ); @@ -514,16 +519,15 @@ async function initKeystore({ defaultAddr }) { return defaultAddr || wifnames[0]; } -/** - * @param {String} insightBaseUrl - */ -async function initCrowdNode(insightBaseUrl) { - if (CrowdNode.main.hotwallet) { +async function initCrowdNode() { + if (CrowdNode._initialized) { return; } process.stdout.write("Checking CrowdNode API... "); await CrowdNode.init({ baseUrl: "https://app.crowdnode.io", + dashsightBaseUrl, + dashsocketBaseUrl, insightBaseUrl, }); console.info(`(hotwallet ${CrowdNode.main.hotwallet})`); @@ -659,7 +663,7 @@ async function mustGetAddr({ defaultAddr }, args) { return [addr, name]; } //let pk = new Dashcore.PrivateKey(wif); - //let addr = pk.toAddress().toString(); + //let addr = (await pk.toAddress()).toString(); return [addr, name]; } @@ -792,8 +796,8 @@ async function generateKey({ defaultKey, plainText }, args) { //@ts-ignore - TODO submit JSDoc PR for Dashcore let pk = new Dashcore.PrivateKey(); - let addr = pk.toAddress().toString(); - let plainWif = pk.toWIF(); + let addr = (await pk.toAddress()).toString(); + let plainWif = await pk.toWIF(); let wif = plainWif; if (!plainText) { @@ -1255,7 +1259,7 @@ async function maybeReadKeyFileRaw(filepath, opts) { } let pk = new Dashcore.PrivateKey(privKey); - let pub = pk.toAddress().toString(); + let pub = (await pk.toAddress()).toString(); return { addr: pub, @@ -1322,7 +1326,7 @@ async function setDefault(_, args) { let filepath = Path.join(keysDir, keyname); let wif = await maybeReadKeyFile(filepath); let pk = new Dashcore.PrivateKey(wif); - let pub = pk.toAddress().toString(); + let pub = (await pk.toAddress()).toString(); console.info("set", defaultWifPath, pub); await Fs.writeFile(defaultWifPath, pub, "utf8"); @@ -1435,7 +1439,7 @@ async function getAllBalances({ dashApi, defaultAddr }, args) { /* let pk = new Dashcore.PrivateKey(wif); - let pub = pk.toAddress().toString(); + let pub = (await pk.toAddress()).toString(); if (`${pub}.wif` !== wifname) { // sanity check warns.push({ @@ -1529,7 +1533,7 @@ async function removeKey({ addr, dashApi, filepath, insightBaseUrl }, args) { return; } - await initCrowdNode(insightBaseUrl); + await initCrowdNode(); let crowdNodeBalance = await CrowdNode.http.GetBalance(addr); if (!crowdNodeBalance) { // may be janky if not registered @@ -1621,18 +1625,18 @@ async function listManagedKeynames() { async function loadAddr({ defaultAddr, insightBaseUrl }, args) { let [addr] = await mustGetAddr({ defaultAddr }, args); - let desiredAmountDash = parseFloat(args.shift() || "0"); - let desiredAmountDuff = Math.round(desiredAmountDash * DUFFS); + let desiredDashAmount = parseFloat(args.shift() || "0"); + let desiredSatoshis = Math.round(desiredDashAmount * SATOSHIS); - let effectiveDuff = desiredAmountDuff; + let effectiveSats = desiredSatoshis; let effectiveDash = ""; - if (!effectiveDuff) { - effectiveDuff = CrowdNode.stakeMinimum + signupTotal + feeEstimate; - effectiveDuff = roundDuff(effectiveDuff, 3); - effectiveDash = toDash(effectiveDuff); + if (!effectiveSats) { + effectiveSats = CrowdNode.stakeMinimum + signupTotal + feeEstimate; + effectiveSats = roundDuff(effectiveSats, 3); + effectiveDash = toDash(effectiveSats); } - await plainLoadAddr({ addr, effectiveDash, effectiveDuff, insightBaseUrl }); + await plainLoadAddr({ addr, effectiveDash, effectiveSats, insightBaseUrl }); return; } @@ -1640,33 +1644,28 @@ async function loadAddr({ defaultAddr, insightBaseUrl }, args) { /** * 1000 to Round to the nearest mDash * ex: 0.50238108 => 0.50300000 - * @param {Number} effectiveDuff + * @param {Number} effectiveSats * @param {Number} numDigits */ -function roundDuff(effectiveDuff, numDigits) { +function roundDuff(effectiveSats, numDigits) { let n = Math.pow(10, numDigits); - let effectiveDash = toDash(effectiveDuff); - effectiveDuff = toDuff( + let effectiveDash = toDash(effectiveSats); + effectiveSats = toDuff( (Math.ceil(parseFloat(effectiveDash) * n) / n).toString(), ); - return effectiveDuff; + return effectiveSats; } /** * @param {Object} opts * @param {String} opts.addr * @param {String} opts.effectiveDash - * @param {Number} opts.effectiveDuff + * @param {Number} opts.effectiveSats * @param {String} opts.insightBaseUrl */ -async function plainLoadAddr({ - addr, - effectiveDash, - effectiveDuff, - insightBaseUrl, -}) { +async function plainLoadAddr({ addr, effectiveDash, effectiveSats }) { console.info(``); - showQr(addr, effectiveDuff); + showQr(addr, effectiveSats); console.info(``); console.info( `Send Đ${effectiveDash} to your staking key via the QR above, or its address:`, @@ -1678,7 +1677,7 @@ async function plainLoadAddr({ console.info(``); console.info(`(waiting...)`); console.info(``); - let payment = await Ws.waitForVout(insightBaseUrl, addr, 0); + let payment = await Ws.waitForVout(dashsocketBaseUrl, addr, 0); console.info(`Received ${payment.satoshis}`); } @@ -1707,7 +1706,7 @@ async function getBalance({ dashApi, defaultAddr }, args) { */ // ex: node ./bin/crowdnode.js transfer Xxxxx 'pub' 0.01 async function transferBalance( - { dashApi, defaultAddr, forceConfirm, insightBaseUrl, insightApi }, + { dashApi, defaultAddr, forceConfirm, insightApi }, args, ) { /** @type Array */ @@ -1719,16 +1718,16 @@ async function transferBalance( // // Ex: // crowdnode transfer {source} {dest} - // crowdnode transfer {source} {dest} {amount} - // crowdnode transfer {dest} {amount} + // crowdnode transfer {source} {dest} {dash-amount} + // crowdnode transfer {dest} {dash-amount} // crowdnode transfer {dest} // - // To disambiguate, we check if the second argument is an amount. + // To disambiguate, we check if the second argument is a dash-amount. if (3 === args.length) { getAddrArgs = args; } else if (2 === args.length) { - let maybeAmount = parseFloat(args[1]); - let isAddr = isNaN(maybeAmount); + let maybeDashAmount = parseFloat(args[1]); + let isAddr = isNaN(maybeDashAmount); if (isAddr) { getAddrArgs = args; } @@ -1738,17 +1737,17 @@ async function transferBalance( let keyname = args.shift() || ""; let newAddr = await wifFileToAddr(keyname); let dashAmount = parseFloat(args.shift() || "0"); - let duffAmount = Math.round(dashAmount * DUFFS); + let satoshis = Math.round(dashAmount * SATOSHIS); let tx; - if (duffAmount) { - tx = await dashApi.createPayment(wif, newAddr, duffAmount); + if (satoshis) { + tx = await dashApi.createPayment(wif, newAddr, satoshis); } else { tx = await dashApi.createBalanceTransfer(wif, newAddr); } - if (duffAmount) { - let dashAmountStr = toDash(duffAmount); + if (satoshis) { + let dashAmountStr = toDash(satoshis); console.info( - `Transferring ${duffAmount} (Đ${dashAmountStr}) to ${newAddr}...`, + `Transferring ${satoshis} (Đ${dashAmountStr}) to ${newAddr}...`, ); } else { console.info(`Transferring balance to ${newAddr}...`); @@ -1764,7 +1763,7 @@ async function transferBalance( } process.exit(1); }, 30 * 1000); - await Ws.waitForVout(insightBaseUrl, newAddr, 0); + await Ws.waitForVout(dashsocketBaseUrl, newAddr, 0); console.info(`Accepted!`); return; } @@ -1778,7 +1777,7 @@ async function transferBalance( */ async function getStatus({ dashApi, defaultAddr, insightBaseUrl }, args) { let [addr] = await mustGetAddr({ defaultAddr }, args); - await initCrowdNode(insightBaseUrl); + await initCrowdNode(); let hotwallet = CrowdNode.main.hotwallet; let state = await getCrowdNodeStatus({ addr, hotwallet }); @@ -1816,7 +1815,7 @@ async function getStatus({ dashApi, defaultAddr, insightBaseUrl }, args) { */ async function sendSignup({ dashApi, defaultAddr, insightBaseUrl }, args) { let [addr, name] = await mustGetAddr({ defaultAddr }, args); - await initCrowdNode(insightBaseUrl); + await initCrowdNode(); let hotwallet = CrowdNode.main.hotwallet; let state = await getCrowdNodeStatus({ addr, hotwallet }); let balanceInfo = await dashApi.getInstantBalance(addr); @@ -1831,7 +1830,7 @@ async function sendSignup({ dashApi, defaultAddr, insightBaseUrl }, args) { let hasEnough = balanceInfo.balanceSat > signupOnly + feeEstimate; if (!hasEnough) { - await collectSignupFees(insightBaseUrl, addr); + await collectSignupFees(addr); } let wif = await maybeReadKeyPaths(name, { wif: true }); @@ -1853,7 +1852,7 @@ async function sendSignup({ dashApi, defaultAddr, insightBaseUrl }, args) { async function acceptTerms({ dashApi, defaultAddr, insightBaseUrl }, args) { let [addr, name] = await mustGetAddr({ defaultAddr }, args); - await initCrowdNode(insightBaseUrl); + await initCrowdNode(); let hotwallet = CrowdNode.main.hotwallet; let state = await getCrowdNodeStatus({ addr, hotwallet }); let balanceInfo = await dashApi.getInstantBalance(addr); @@ -1875,7 +1874,7 @@ async function acceptTerms({ dashApi, defaultAddr, insightBaseUrl }, args) { } let hasEnough = balanceInfo.balanceSat > acceptOnly + feeEstimate; if (!hasEnough) { - await collectSignupFees(insightBaseUrl, addr); + await collectSignupFees(addr); } let wif = await maybeReadKeyPaths(name, { wif: true }); @@ -1895,12 +1894,9 @@ async function acceptTerms({ dashApi, defaultAddr, insightBaseUrl }, args) { * @param {Boolean} opts.noReserve * @param {Array} args */ -async function depositDash( - { dashApi, defaultAddr, insightBaseUrl, noReserve }, - args, -) { +async function depositDash({ dashApi, defaultAddr, noReserve }, args) { let [addr, name] = await mustGetAddr({ defaultAddr }, args); - await initCrowdNode(insightBaseUrl); + await initCrowdNode(); let hotwallet = CrowdNode.main.hotwallet; let state = await getCrowdNodeStatus({ addr, hotwallet }); let balanceInfo = await dashApi.getInstantBalance(addr); @@ -1926,20 +1922,20 @@ async function depositDash( // deposit what the user asks, or all that we have, // or all that the user deposits - but at least 2x the reserve - let desiredAmountDash = parseFloat(args.shift() || "0"); - let desiredAmountDuff = Math.round(desiredAmountDash * DUFFS); - let effectiveAmount = desiredAmountDuff; - if (!effectiveAmount) { - effectiveAmount = balanceInfo.balanceSat - reserve; + let desiredDashAmount = parseFloat(args.shift() || "0"); + let desiredSatoshis = Math.round(desiredDashAmount * SATOSHIS); + let effectiveSats = desiredSatoshis; + if (!effectiveSats) { + effectiveSats = balanceInfo.balanceSat - reserve; } - let needed = Math.max(reserve * 2, effectiveAmount + reserve); + let needed = Math.max(reserve * 2, effectiveSats + reserve); if (balanceInfo.balanceSat < needed) { let ask = 0; - if (desiredAmountDuff) { - ask = desiredAmountDuff + reserve + -balanceInfo.balanceSat; + if (desiredSatoshis) { + ask = desiredSatoshis + reserve + -balanceInfo.balanceSat; } - await collectDeposit(insightBaseUrl, addr, ask); + await collectDeposit(addr, ask); balanceInfo = await dashApi.getInstantBalance(addr); if (balanceInfo.balanceSat < needed) { let balanceDash = toDash(balanceInfo.balanceSat); @@ -1950,18 +1946,16 @@ async function depositDash( return; } } - if (!desiredAmountDuff) { - effectiveAmount = balanceInfo.balanceSat - reserve; + if (!desiredSatoshis) { + effectiveSats = balanceInfo.balanceSat - reserve; } - let effectiveDash = toDash(effectiveAmount); - console.info( - `Initiating deposit of ${effectiveAmount} (Đ${effectiveDash})...`, - ); + let effectiveDash = toDash(effectiveSats); + console.info(`Initiating deposit of ${effectiveSats} (Đ${effectiveDash})...`); let wif = await maybeReadKeyPaths(name, { wif: true }); - await CrowdNode.deposit(wif, hotwallet, effectiveAmount); + await CrowdNode.deposit(wif, hotwallet, effectiveSats); state.deposit = DONE; console.info(` ${state.deposit} DepositReceived`); return; @@ -1976,7 +1970,7 @@ async function depositDash( */ async function withdrawDash({ dashApi, defaultAddr, insightBaseUrl }, args) { let [addr] = await mustGetAddr({ defaultAddr }, args); - await initCrowdNode(insightBaseUrl); + await initCrowdNode(); let hotwallet = CrowdNode.main.hotwallet; let state = await getCrowdNodeStatus({ addr, hotwallet }); @@ -2008,7 +2002,7 @@ async function withdrawDash({ dashApi, defaultAddr, insightBaseUrl }, args) { let filepath = Path.join(keysDir, wifname); let wif = await maybeReadKeyFile(filepath); let paid = await CrowdNode.withdraw(wif, hotwallet, permil); - //let paidFloat = (paid.satoshis / DUFFS).toFixed(8); + //let paidFloat = (paid.satoshis / SATOSHIS).toFixed(8); //let paidInt = paid.satoshis.toString().padStart(9, "0"); console.info(`API Response: ${paid.api}`); return; @@ -2042,15 +2036,14 @@ async function wifFileToAddr(name) { } let pk = new Dashcore.PrivateKey(privKey); - let pub = pk.toPublicKey().toAddress().toString(); + let pub = (await pk.toPublicKey().toAddress()).toString(); return pub; } /** - * @param {String} insightBaseUrl * @param {String} addr */ -async function collectSignupFees(insightBaseUrl, addr) { +async function collectSignupFees(addr) { console.info(``); showQr(addr); @@ -2068,23 +2061,23 @@ async function collectSignupFees(insightBaseUrl, addr) { console.info(""); console.info("(waiting...)"); console.info(""); - let payment = await Ws.waitForVout(insightBaseUrl, addr, 0); + let payment = await Ws.waitForVout(dashsocketBaseUrl, addr, 0); console.info(`Received ${payment.satoshis}`); } /** * @param {String} insightBaseUrl * @param {String} addr - * @param {Number} duffAmount + * @param {Number} satoshis */ -async function collectDeposit(insightBaseUrl, addr, duffAmount) { +async function collectDeposit(addr, satoshis) { console.info(``); - showQr(addr, duffAmount); + showQr(addr, satoshis); let depositMsg = `Please send what you wish to deposit to ${addr}`; - if (duffAmount) { - let dashAmount = toDash(duffAmount); - depositMsg = `Please deposit ${duffAmount} (Đ${dashAmount}) to ${addr}`; + if (satoshis) { + let dashAmount = toDash(satoshis); + depositMsg = `Please deposit ${satoshis} (Đ${dashAmount}) to ${addr}`; } let msgPad = Math.ceil((qrWidth - depositMsg.length) / 2); @@ -2097,7 +2090,7 @@ async function collectDeposit(insightBaseUrl, addr, duffAmount) { console.info(""); console.info("(waiting...)"); console.info(""); - let payment = await Ws.waitForVout(insightBaseUrl, addr, 0); + let payment = await Ws.waitForVout(dashsocketBaseUrl, addr, 0); console.info(`Received ${payment.satoshis}`); } @@ -2116,14 +2109,14 @@ function emptyStringOnErrEnoent(err) { * @param {Number} duffs - ex: 00000000 */ function toDash(duffs) { - return (duffs / DUFFS).toFixed(8); + return (duffs / SATOSHIS).toFixed(8); } /** * @param {Number} duffs - ex: 00000000 */ function toDASH(duffs) { - let dash = (duffs / DUFFS).toFixed(8); + let dash = (duffs / SATOSHIS).toFixed(8); return `Đ` + dash.padStart(12, " "); } @@ -2131,7 +2124,7 @@ function toDASH(duffs) { * @param {String} dash - ex: 0.00000000 */ function toDuff(dash) { - return Math.round(parseFloat(dash) * DUFFS); + return Math.round(parseFloat(dash) * SATOSHIS); } // Run diff --git a/bin/crowdnode.js.bak b/bin/crowdnode.js.bak deleted file mode 100755 index e59b950..0000000 --- a/bin/crowdnode.js.bak +++ /dev/null @@ -1,2360 +0,0 @@ -#!/usr/bin/env node -"use strict"; -/*jshint maxcomplexity:25 */ - -require("dotenv").config({ path: ".env" }); -require("dotenv").config({ path: ".env.secret" }); - -let HOME = process.env.HOME || ""; - -//@ts-ignore -let pkg = require("../package.json"); - -let Fs = require("fs").promises; -let Path = require("path"); - -let Cipher = require("./_cipher.js"); -let CrowdNode = require("../lib/crowdnode.js"); -let Dash = require("../lib/dash.js"); -let Insight = require("../lib/insight.js"); -let Prompt = require("./_prompt.js"); -let Qr = require("../lib/qr.js"); -let Ws = require("../lib/ws.js"); - -let Dashcore = require("@dashevo/dashcore-lib"); - -const DONE = "✅"; -const TODO = "ℹ️"; -const NO_SHADOW = "NONE"; -const DUFFS = 100000000; - -let shownDefault = false; -let qrWidth = 2 + 33 + 2; -// Sign Up Fees: -// 0.00236608 // required for signup -// 0.00002000 // TX fee estimate -// 0.00238608 // minimum recommended amount -// Target: -// 0.01000000 -let signupOnly = CrowdNode.requests.signupForApi + CrowdNode.requests.offset; -let acceptOnly = CrowdNode.requests.acceptTerms + CrowdNode.requests.offset; -let signupFees = signupOnly + acceptOnly; -let feeEstimate = 500; -let signupTotal = signupFees + 2 * feeEstimate; - -//let paths = {}; -let configdir = `.config/crowdnode`; -let keysDir = Path.join(HOME, `${configdir}/keys`); -let keysDirRel = `~/${configdir}/keys`; -let shadowPath = Path.join(HOME, `${configdir}/shadow`); -let defaultWifPath = Path.join(HOME, `${configdir}/default`); - -function debug() { - //@ts-ignore - console.error.apply(console, arguments); -} - -function showVersion() { - console.info(`${pkg.name} v${pkg.version} - ${pkg.description}`); - console.info(); -} - -function showHelp() { - showVersion(); - - console.info("Quick Start:"); - // technically this also has [--no-reserve] - console.info(" crowdnode stake [addr-or-import-key | --create-new]"); - - console.info(""); - console.info("Usage:"); - console.info(" crowdnode help"); - console.info(" crowdnode version"); - console.info(""); - console.info(" crowdnode status [keyfile-or-addr]"); - console.info(" crowdnode signup [keyfile-or-addr]"); - console.info(" crowdnode accept [keyfile-or-addr]"); - console.info( - " crowdnode deposit [keyfile-or-addr] [dash-amount] [--no-reserve]", - ); - console.info( - " crowdnode withdraw [keyfile-or-addr] # 1.0-100.0 (steps by 0.1)", - ); - console.info(""); - - console.info("Helpful Extras:"); - console.info(" crowdnode balance [keyfile-or-addr]"); // addr - console.info(" crowdnode load [keyfile-or-addr] [dash-amount]"); // addr - console.info( - " crowdnode transfer [dash-amount]", - ); // custom - console.info(""); - - console.info("Key Management & Encryption:"); - console.info(" crowdnode init"); - console.info(" crowdnode generate [--plain-text] [./privkey.wif]"); - console.info(" crowdnode encrypt"); // TODO allow encrypting one-by-one? - console.info(" crowdnode list"); - console.info(" crowdnode use "); - console.info(" crowdnode import "); - //console.info(" crowdnode import <(dash-cli dumpprivkey )"); // TODO - //console.info(" crowdnode export "); // TODO - console.info(" crowdnode passphrase # set or change passphrase"); - console.info(" crowdnode decrypt"); // TODO allow decrypting one-by-one? - console.info(" crowdnode delete "); - console.info(""); - - console.info("CrowdNode HTTP RPC:"); - console.info(" crowdnode http FundsOpen "); - console.info(" crowdnode http VotingOpen "); - console.info(" crowdnode http GetFunds "); - console.info(" crowdnode http GetFundsFrom "); - console.info(" crowdnode http GetBalance "); - console.info(" crowdnode http GetMessages "); - console.info(" crowdnode http IsAddressInUse "); - // TODO create signature rather than requiring it - console.info(" crowdnode http SetEmail ./privkey.wif "); - console.info(" crowdnode http Vote ./privkey.wif "); - console.info(" "); - console.info( - " crowdnode http SetReferral ./privkey.wif ", - ); - console.info(""); - console.info("Official CrowdNode Resources"); - console.info(""); - console.info("Homepage:"); - console.info(" https://crowdnode.io/"); - console.info(""); - console.info("Terms of Service:"); - console.info(" https://crowdnode.io/terms/"); - console.info(""); - console.info("BlockChain API Guide:"); - console.info( - " https://knowledge.crowdnode.io/en/articles/5963880-blockchain-api-guide", - ); - console.info(""); -} - -let cmds = {}; - -async function main() { - /*jshint maxcomplexity:40 */ - /*jshint maxstatements:500 */ - - // Usage: - // crowdnode [flags] [options] - // Example: - // crowdnode withdraw ./Xxxxpubaddr.wif 100.0 - - let args = process.argv.slice(2); - - // flags - let forceGenerate = removeItem(args, "--create-new"); - let forceConfirm = removeItem(args, "--unconfirmed"); - let plainText = removeItem(args, "--plain-text"); - let noReserve = removeItem(args, "--no-reserve"); - - let subcommand = args.shift(); - - if (!subcommand || ["--help", "-h", "help"].includes(subcommand)) { - showHelp(); - process.exit(0); - return; - } - - if (["--version", "-V", "version"].includes(subcommand)) { - showVersion(); - process.exit(0); - return; - } - - // - // - // find addr by name or by file or by string - await Fs.mkdir(keysDir, { - recursive: true, - }); - - let defaultAddr = await Fs.readFile(defaultWifPath, "utf8").catch( - emptyStringOnErrEnoent, - ); - defaultAddr = defaultAddr.trim(); - - let insightBaseUrl = - process.env.INSIGHT_BASE_URL || "https://insight.dash.org"; - let insightApi = Insight.create({ baseUrl: insightBaseUrl }); - let dashApi = Dash.create({ insightApi: insightApi }); - - if ("stake" === subcommand) { - await stakeDash( - { - dashApi, - insightApi, - insightBaseUrl, - defaultAddr, - forceGenerate, - noReserve, - }, - args, - ); - process.exit(0); - return; - } - - if ("list" === subcommand) { - await listKeys({ dashApi, defaultAddr }, args); - process.exit(0); - return; - } - - if ("init" === subcommand) { - await initKeystore({ defaultAddr }); - process.exit(0); - return; - } - - if ("generate" === subcommand) { - await generateKey({ defaultKey: defaultAddr, plainText }, args); - process.exit(0); - return; - } - - if ("passphrase" === subcommand) { - await setPassphrase({}, args); - process.exit(0); - return; - } - - if ("import" === subcommand) { - let keypath = args.shift() || ""; - await importKey({ keypath }); - process.exit(0); - return; - } - - if ("encrypt" === subcommand) { - let addr = args.shift() || ""; - if (!addr) { - await encryptAll(null); - process.exit(0); - return; - } - - let keypath = await findWif(addr); - if (!keypath) { - console.error(`no managed key matches '${addr}'`); - process.exit(1); - return; - } - let key = await maybeReadKeyFileRaw(keypath); - if (!key) { - throw new Error("impossible error"); - } - await encryptAll([key]); - process.exit(0); - return; - } - - if ("decrypt" === subcommand) { - let addr = args.shift() || ""; - if (!addr) { - await decryptAll(null); - await Fs.writeFile(shadowPath, NO_SHADOW, "utf8").catch( - emptyStringOnErrEnoent, - ); - process.exit(0); - return; - } - let keypath = await findWif(addr); - if (!keypath) { - console.error(`no managed key matches '${addr}'`); - process.exit(1); - return; - } - let key = await maybeReadKeyFileRaw(keypath); - if (!key) { - throw new Error("impossible error"); - } - await decryptAll([key]); - process.exit(0); - return; - } - - // use or select or default... ? - if ("use" === subcommand) { - await setDefault(null, args); - process.exit(0); - return; - } - - // helper for debugging - if ("transfer" === subcommand) { - await transferBalance( - { dashApi, defaultAddr, forceConfirm, insightBaseUrl, insightApi }, - args, - ); - process.exit(0); - return; - } - - let rpc = ""; - if ("http" === subcommand) { - rpc = args.shift() || ""; - if (!rpc) { - showHelp(); - process.exit(1); - return; - } - - let [addr] = await mustGetAddr({ defaultAddr }, args); - - await initCrowdNode(insightBaseUrl); - // ex: http (, ...) - args.unshift(addr); - let hasRpc = rpc in CrowdNode.http; - if (!hasRpc) { - console.error(`Unrecognized rpc command ${rpc}`); - console.error(); - showHelp(); - process.exit(1); - } - //@ts-ignore - TODO use `switch` or make Record Type - let result = await CrowdNode.http[rpc].apply(null, args); - console.info(``); - console.info(`${rpc} ${addr}:`); - if ("string" === typeof result) { - console.info(result); - } else { - console.info(JSON.stringify(result, null, 2)); - } - process.exit(0); - return; - } - - if ("load" === subcommand) { - await loadAddr({ defaultAddr, insightBaseUrl }, args); - process.exit(0); - return; - } - - // keeping rm for backwards compat - if ("rm" === subcommand || "delete" === subcommand) { - await initCrowdNode(insightBaseUrl); - let [addr, filepath] = await mustGetAddr({ defaultAddr }, args); - await removeKey({ addr, dashApi, filepath, insightBaseUrl }, args); - process.exit(0); - return; - } - - if ("balance" === subcommand) { - if (args.length) { - await getBalance({ dashApi, defaultAddr }, args); - process.exit(0); - return; - } - - await getAllBalances({ dashApi, defaultAddr }, args); - process.exit(0); - return; - } - - if ("status" === subcommand) { - await getStatus({ dashApi, defaultAddr, insightBaseUrl }, args); - process.exit(0); - return; - } - - if ("signup" === subcommand) { - await sendSignup({ dashApi, defaultAddr, insightBaseUrl }, args); - process.exit(0); - return; - } - - if ("accept" === subcommand) { - await acceptTerms({ dashApi, defaultAddr, insightBaseUrl }, args); - process.exit(0); - return; - } - - if ("deposit" === subcommand) { - await depositDash( - { dashApi, defaultAddr, insightBaseUrl, noReserve }, - args, - ); - process.exit(0); - return; - } - - // The misspelling 'withdrawal' is kept as part of compatibility < v1.7 - if ("withdrawal" === subcommand) { - console.warn( - `[Deprecation Notice] 'crowdnode withdrawal' is a misspelling of 'crowdnode withdraw'`, - ); - subcommand = "withdraw"; - } - - if ("withdraw" === subcommand) { - await withdrawDash({ dashApi, defaultAddr, insightBaseUrl }, args); - process.exit(0); - return; - } - - console.error(`Unrecognized subcommand ${subcommand}`); - console.error(); - showHelp(); - process.exit(1); -} - -/** - * @param {String} insightBaseUrl - * @param {String} addr - */ -async function collectSignupFees(insightBaseUrl, addr) { - console.info(``); - showQr(addr); - - let signupTotalDash = toDash(signupTotal); - let signupMsg = `Please send >= ${signupTotal} (Đ${signupTotalDash}) to Sign Up to CrowdNode`; - let msgPad = Math.ceil((qrWidth - signupMsg.length) / 2); - let subMsg = "(plus whatever you'd like to deposit)"; - let subMsgPad = Math.ceil((qrWidth - subMsg.length) / 2); - - console.info(); - console.info(" ".repeat(msgPad) + signupMsg); - console.info(" ".repeat(subMsgPad) + subMsg); - console.info(); - - console.info(""); - console.info("(waiting...)"); - console.info(""); - let payment = await Ws.waitForVout(insightBaseUrl, addr, 0); - console.info(`Received ${payment.satoshis}`); -} - -/** - * @param {String} insightBaseUrl - * @param {String} addr - * @param {Number} duffAmount - */ -async function collectDeposit(insightBaseUrl, addr, duffAmount) { - console.info(``); - showQr(addr, duffAmount); - - let depositMsg = `Please send what you wish to deposit to ${addr}`; - if (duffAmount) { - let dashAmount = toDash(duffAmount); - depositMsg = `Please deposit ${duffAmount} (Đ${dashAmount}) to ${addr}`; - } - - let msgPad = Math.ceil((qrWidth - depositMsg.length) / 2); - msgPad = Math.max(0, msgPad); - - console.info(); - console.info(" ".repeat(msgPad) + depositMsg); - console.info(); - - console.info(""); - console.info("(waiting...)"); - console.info(""); - let payment = await Ws.waitForVout(insightBaseUrl, addr, 0); - console.info(`Received ${payment.satoshis}`); -} - -/** - * @param {Object} opts - * @param {any} opts.dashApi - TODO - * @param {String} opts.defaultAddr - * @param {String} opts.insightBaseUrl - * @param {Array} args - */ -async function getStatus({ dashApi, defaultAddr, insightBaseUrl }, args) { - let [addr] = await mustGetAddr({ defaultAddr }, args); - await initCrowdNode(insightBaseUrl); - let hotwallet = CrowdNode.main.hotwallet; - let state = await getCrowdNodeStatus({ addr, hotwallet }); - - console.info(); - console.info(`API Actions Complete for ${addr}:`); - console.info(` ${state.signup} SignUpForApi`); - console.info(` ${state.accept} AcceptTerms`); - console.info(` ${state.deposit} DepositReceived`); - console.info(); - let crowdNodeBalance = await CrowdNode.http.GetBalance(addr); - // may be unregistered / undefined - /* - * { - * '@odata.context': 'https://app.crowdnode.io/odata/$metadata#Edm.String', - * value: 'Address not found.' - * } - */ - if (!crowdNodeBalance.TotalBalance) { - crowdNodeBalance.TotalBalance = 0; - } - let crowdNodeDuff = toDuff(crowdNodeBalance.TotalBalance); - console.info( - `CrowdNode Stake: ${crowdNodeDuff} (Đ${crowdNodeBalance.TotalBalance})`, - ); - console.info(); - return; -} - -/** - * @param {Object} opts - * @param {any} opts.dashApi - TODO - * @param {String} opts.defaultAddr - * @param {String} opts.insightBaseUrl - * @param {Array} args - */ -async function sendSignup({ dashApi, defaultAddr, insightBaseUrl }, args) { - let [addr, name] = await mustGetAddr({ defaultAddr }, args); - await initCrowdNode(insightBaseUrl); - let hotwallet = CrowdNode.main.hotwallet; - let state = await getCrowdNodeStatus({ addr, hotwallet }); - let balanceInfo = await checkBalance({ addr, dashApi }); - - if (state.status?.signup) { - console.info(`${addr} is already signed up. Here's the account status:`); - console.info(` ${state.signup} SignUpForApi`); - console.info(` ${state.accept} AcceptTerms`); - console.info(` ${state.deposit} DepositReceived`); - return; - } - - let hasEnough = balanceInfo.balanceSat > signupOnly + feeEstimate; - if (!hasEnough) { - await collectSignupFees(insightBaseUrl, addr); - } - - let wif = await maybeReadKeyPaths(name, { wif: true }); - - console.info("Requesting account..."); - await CrowdNode.signup(wif, hotwallet); - state.signup = DONE; - console.info(` ${state.signup} SignUpForApi`); - return; -} - -/** - * @param {Object} opts - * @param {any} opts.dashApi - TODO - * @param {String} opts.defaultAddr - * @param {String} opts.insightBaseUrl - * @param {Array} args - */ -async function acceptTerms({ dashApi, defaultAddr, insightBaseUrl }, args) { - let [addr, name] = await mustGetAddr({ defaultAddr }, args); - - await initCrowdNode(insightBaseUrl); - let hotwallet = CrowdNode.main.hotwallet; - let state = await getCrowdNodeStatus({ addr, hotwallet }); - let balanceInfo = await dashApi.getInstantBalance(addr); - - if (!state.status?.signup) { - console.info(`${addr} is not signed up yet. Here's the account status:`); - console.info(` ${state.signup} SignUpForApi`); - console.info(` ${state.accept} AcceptTerms`); - process.exit(1); - return; - } - - if (state.status?.accept) { - console.info(`${addr} is already signed up. Here's the account status:`); - console.info(` ${state.signup} SignUpForApi`); - console.info(` ${state.accept} AcceptTerms`); - console.info(` ${state.deposit} DepositReceived`); - return; - } - let hasEnough = balanceInfo.balanceSat > acceptOnly + feeEstimate; - if (!hasEnough) { - await collectSignupFees(insightBaseUrl, addr); - } - - let wif = await maybeReadKeyPaths(name, { wif: true }); - - console.info("Accepting terms..."); - await CrowdNode.accept(wif, hotwallet); - state.accept = DONE; - console.info(` ${state.accept} AcceptTerms`); - return; -} - -/** - * @param {Object} opts - * @param {any} opts.dashApi - TODO - * @param {String} opts.defaultAddr - * @param {String} opts.insightBaseUrl - * @param {Boolean} opts.noReserve - * @param {Array} args - */ -async function depositDash( - { dashApi, defaultAddr, insightBaseUrl, noReserve }, - args, -) { - let [addr, name] = await mustGetAddr({ defaultAddr }, args); - await initCrowdNode(insightBaseUrl); - let hotwallet = CrowdNode.main.hotwallet; - let state = await getCrowdNodeStatus({ addr, hotwallet }); - let balanceInfo = await dashApi.getInstantBalance(addr); - - if (!state.status?.accept) { - console.error(`no account for address ${addr}`); - process.exit(1); - return; - } - - // this would allow for at least 2 withdrawals costing (21000 + 1000) - let reserve = 50000; - let reserveDash = toDash(reserve); - if (!noReserve) { - console.info( - `reserving ${reserve} (Đ${reserveDash}) for withdrawals (--no-reserve to disable)`, - ); - } else { - reserve = 0; - } - - // TODO if unconfirmed, check utxos instead - - // deposit what the user asks, or all that we have, - // or all that the user deposits - but at least 2x the reserve - let desiredAmountDash = parseFloat(args.shift() || "0"); - let desiredAmountDuff = Math.round(desiredAmountDash * DUFFS); - let effectiveAmount = desiredAmountDuff; - if (!effectiveAmount) { - effectiveAmount = balanceInfo.balanceSat - reserve; - } - let needed = Math.max(reserve * 2, effectiveAmount + reserve); - - if (balanceInfo.balanceSat < needed) { - let ask = 0; - if (desiredAmountDuff) { - ask = desiredAmountDuff + reserve + -balanceInfo.balanceSat; - } - await collectDeposit(insightBaseUrl, addr, ask); - balanceInfo = await dashApi.getInstantBalance(addr); - if (balanceInfo.balanceSat < needed) { - let balanceDash = toDash(balanceInfo.balanceSat); - console.error( - `Balance is still too small: ${balanceInfo.balanceSat} (Đ${balanceDash})`, - ); - process.exit(1); - return; - } - } - if (!desiredAmountDuff) { - effectiveAmount = balanceInfo.balanceSat - reserve; - } - - let effectiveDash = toDash(effectiveAmount); - console.info( - `Initiating deposit of ${effectiveAmount} (Đ${effectiveDash})...`, - ); - - let wif = await maybeReadKeyPaths(name, { wif: true }); - - await CrowdNode.deposit(wif, hotwallet, effectiveAmount); - state.deposit = DONE; - console.info(` ${state.deposit} DepositReceived`); - return; -} - -/** - * @param {Object} opts - * @param {any} opts.dashApi - TODO - * @param {String} opts.defaultAddr - * @param {Boolean} opts.forceGenerate - * @param {String} opts.insightBaseUrl - * @param {any} opts.insightApi - * @param {Boolean} opts.noReserve - * @param {Array} args - */ -async function stakeDash( - { - dashApi, - defaultAddr, - forceGenerate, - insightApi, - insightBaseUrl, - noReserve, - }, - args, -) { - let err = await Fs.access(args[0]).catch(Object); - let addr; - if (!err) { - let keypath = args.shift() || ""; - addr = await importKey({ keypath }); - } else if (forceGenerate) { - addr = await generateKey({ defaultKey: defaultAddr }, []); - } else { - addr = await initKeystore({ defaultAddr }); - } - - if (!addr) { - let [_addr] = await mustGetAddr({ defaultAddr }, args); - addr = _addr; - } - - let extra = feeEstimate; - console.info("Checking CrowdNode account... "); - await CrowdNode.init({ - baseUrl: "https://app.crowdnode.io", - insightBaseUrl, - }); - let hotwallet = CrowdNode.main.hotwallet; - let state = await getCrowdNodeStatus({ addr, hotwallet }); - - if (!state.status?.accept) { - if (!state.status?.signup) { - let signUpDeposit = signupOnly + feeEstimate; - console.info( - ` ${TODO} SignUpForApi deposit is ${signupOnly} (+ tx fee)`, - ); - extra += signUpDeposit; - } else { - console.info(` ${DONE} SignUpForApi complete`); - } - let acceptDeposit = acceptOnly + feeEstimate; - console.info(` ${TODO} AcceptTerms deposit is ${acceptOnly} (+ tx fee)`); - extra += acceptDeposit; - } - - let desiredAmountDash = args.shift() || "0.5"; - let effectiveDuff = toDuff(desiredAmountDash); - effectiveDuff += extra; - - let balanceInfo = await dashApi.getInstantBalance(addr); - effectiveDuff -= balanceInfo.balanceSat; - - if (effectiveDuff > 0) { - effectiveDuff = roundDuff(effectiveDuff, 3); - let effectiveDash = toDash(effectiveDuff); - await plainLoadAddr({ - addr, - effectiveDash, - effectiveDuff, - insightBaseUrl, - }); - } - - if (!state.status?.accept) { - if (!state.status?.signup) { - await sendSignup({ dashApi, defaultAddr: addr, insightBaseUrl }, [addr]); - } - await acceptTerms({ dashApi, defaultAddr: addr, insightBaseUrl }, [addr]); - } - - await depositDash( - { dashApi, defaultAddr: addr, insightBaseUrl, noReserve }, - [addr].concat(args), - ); - - await checkBalance({ addr, dashApi }); -} - -/** - * @param {Object} opts - * @param {any} opts.dashApi - TODO - * @param {String} opts.defaultAddr - * @param {String} opts.insightBaseUrl - * @param {Array} args - */ -async function withdrawalDash({ dashApi, defaultAddr, insightBaseUrl }, args) { - let [addr] = await mustGetAddr({ defaultAddr }, args); - await initCrowdNode(insightBaseUrl); - let hotwallet = CrowdNode.main.hotwallet; - let state = await getCrowdNodeStatus({ addr, hotwallet }); - - if (!state.status?.accept) { - console.error(`no account for address ${addr}`); - process.exit(1); - return; - } - - let percentStr = args.shift() || "100.0"; - // pass: .1 0.1, 1, 1.0, 10, 10.0, 100, 100.0 - // fail: 1000, 10.00 - if (!/^1?\d?\d?(\.\d)?$/.test(percentStr)) { - console.error("Error: withdrawal percent must be between 0.1 and 100.0"); - process.exit(1); - } - let percent = parseFloat(percentStr); - - let permil = Math.round(percent * 10); - if (permil <= 0 || permil > 1000) { - console.error("Error: withdrawal percent must be between 0.1 and 100.0"); - process.exit(1); - } - - let realPercentStr = (permil / 10).toFixed(1); - console.info(`Initiating withdrawal of ${realPercentStr}%...`); - - let wifname = await findWif(addr); - let filepath = Path.join(keysDir, wifname); - let wif = await maybeReadKeyFile(filepath); - let paid = await CrowdNode.withdrawal(wif, hotwallet, permil); - //let paidFloat = (paid.satoshis / DUFFS).toFixed(8); - //let paidInt = paid.satoshis.toString().padStart(9, "0"); - console.info(`API Response: ${paid.api}`); - return; -} - -/** - * @param {String} insightBaseUrl - */ -async function initCrowdNode(insightBaseUrl) { - if (CrowdNode.main.hotwallet) { - return; - } - process.stdout.write("Checking CrowdNode API... "); - await CrowdNode.init({ - baseUrl: "https://app.crowdnode.io", - insightBaseUrl, - }); - console.info(`(hotwallet ${CrowdNode.main.hotwallet})`); -} - -/** - * @param {Object} opts - * @param {String} opts.addr - * @param {String} opts.hotwallet - */ -async function getCrowdNodeStatus({ addr, hotwallet }) { - let state = { - signup: TODO, - accept: TODO, - deposit: TODO, - status: { - signup: 0, - accept: 0, - deposit: 0, - }, - }; - - //@ts-ignore - TODO why warnings? - let status = await CrowdNode.status(addr, hotwallet); - if (status) { - state.status = status; - } - if (state.status?.signup) { - state.signup = DONE; - } - if (state.status?.accept) { - state.accept = DONE; - } - if (state.status?.deposit) { - state.deposit = DONE; - } - return state; -} - -/** - * @param {Null} _ - * @param {Array} args - */ -async function setDefault(_, args) { - let addr = args.shift() || ""; - - let keyname = await findWif(addr); - if (!keyname) { - console.error(`no key matches '${addr}'`); - process.exit(1); - return; - } - - let filepath = Path.join(keysDir, keyname); - let wif = await maybeReadKeyFile(filepath); - let pk = new Dashcore.PrivateKey(wif); - let pub = pk.toAddress().toString(); - - console.info("set", defaultWifPath, pub); - await Fs.writeFile(defaultWifPath, pub, "utf8"); -} - -/** - * @param {String} defaultAddr - * @param {Object} [opts] - * @param {Boolean} opts.wif - */ -async function mustGetDefaultWif(defaultAddr, opts) { - let defaultWif = ""; - if (defaultAddr) { - let keyfile = Path.join(keysDir, `${defaultAddr}.wif`); - let raw = await maybeReadKeyFileRaw(keyfile, opts); - // misnomering wif here a bit - defaultWif = raw?.wif || raw?.addr || ""; - } - if (defaultWif && !shownDefault) { - shownDefault = true; - debug(`Selected default staking key ${defaultAddr}`); - return defaultWif; - } - - console.error(); - console.error(`Error: no default staking key selected.`); - console.error(); - console.error(`Select a different address:`); - console.error(` crowdnode list`); - console.error(` crowdnode use `); - console.error(``); - console.error(`Or create a new staking key:`); - console.error(` crowdnode generate`); - console.error(); - process.exit(1); - return ""; -} - - - -/** - * @param {String} addr - Base58Check pubKeyHash address - * @param {Number} duffs - 1/100000000 of a DASH - */ -function showQr(addr, duffs = 0) { - let dashAmount = toDash(duffs); - let dashUri = `dash://${addr}`; - if (duffs) { - dashUri += `?amount=${dashAmount}`; - } - - let dashQr = Qr.ascii(dashUri, { indent: 4, size: "mini" }); - let addrPad = Math.max(0, Math.ceil((qrWidth - dashUri.length) / 2)); - - console.info(dashQr); - console.info(); - console.info(" ".repeat(addrPad) + dashUri); -} - -/** - * @param {Object} opts - * @param {any} opts.dashApi - TODO - * @param {String} opts.defaultAddr - * @param {Array} args - */ -async function getAllBalances({ dashApi, defaultAddr }, args) { - let wifnames = await listManagedKeynames(); - let totals = { - key: 0, - stake: 0, - dividend: 0, - keyDash: "", - stakeDash: "", - dividendDash: "", - }; - - if (wifnames.length) { - // to print 'default staking key' message - await mustGetAddr({ defaultAddr }, args); - } - - /** - * @type Array<{ node: String, error: Error }> - */ - let warns = []; - // console.error because console.debug goes to stdout, not stderr - debug(``); - debug(`Staking keys: (in ${keysDirRel}/)`); - debug(``); - console.info( - `| | 🔑 Holdings | 🪧 Stakings | 💸 Earnings |`, - ); - console.info( - `| ---------------------------------: | ------------: | ------------: | ------------: |`, - ); - if (!wifnames.length) { - console.info(` (none)`); - } - await wifnames.reduce(async function (promise, wifname) { - await promise; - - let wifpath = Path.join(keysDir, wifname); - let addr = await maybeReadKeyFile(wifpath, { wif: false }).catch(function ( - err, - ) { - warns.push({ node: wifname, error: err }); - return ""; - }); - if (!addr) { - return; - } - - /* - let pk = new Dashcore.PrivateKey(wif); - let pub = pk.toAddress().toString(); - if (`${pub}.wif` !== wifname) { - // sanity check - warns.push({ - node: wifname, - error: new Error( - `computed pubkey '${pub}' of WIF does not match filename '${wifname}'`, - ), - }); - return; - } - */ - - process.stdout.write(`| ${addr} |`); - - let balanceInfo = await dashApi.getInstantBalance(addr); - let balanceDASH = toDASH(balanceInfo.balanceSat); - - let crowdNodeBalance = await CrowdNode.http.GetBalance(addr); - if (!crowdNodeBalance.TotalBalance) { - crowdNodeBalance.TotalBalance = 0; - crowdNodeBalance.TotalDividend = 0; - } - let crowdNodeDuffNum = toDuff(crowdNodeBalance.TotalBalance); - let crowdNodeDASH = toDASH(crowdNodeDuffNum); - - let crowdNodeDivNum = toDuff(crowdNodeBalance.TotalDividend); - let crowdNodeDivDASH = toDASH(crowdNodeDivNum); - process.stdout.write( - ` ${balanceDASH} | ${crowdNodeDASH} | ${crowdNodeDivDASH} |`, - ); - - totals.key += balanceInfo.balanceSat; - totals.dividend += crowdNodeBalance.TotalDividend; - totals.stake += crowdNodeBalance.TotalBalance; - - console.info(); - }, Promise.resolve()); - console.info( - `| | | | |`, - ); - let total = `| Totals`; - totals.keyDash = toDASH(toDuff(totals.key.toString())); - totals.stakeDash = toDASH(toDuff(totals.stake.toString())); - totals.dividendDash = toDASH(toDuff(totals.dividend.toString())); - console.info( - `${total} | ${totals.stakeDash} | ${totals.stakeDash} | ${totals.dividendDash} |`, - ); - debug(``); - - if (warns.length) { - console.warn(`Warnings:`); - warns.forEach(function (warn) { - console.warn(`${warn.node}: ${warn.error.message}`); - }); - console.warn(``); - } -} - - - -/** - * @param {Object} opts - * @param {String} opts.defaultAddr - * @param {any} opts.dashApi - TODO - * @param {Array} args - */ -async function getBalance({ dashApi, defaultAddr }, args) { - let [addr] = await mustGetAddr({ defaultAddr }, args); - await checkBalance({ addr, dashApi }); - //let balanceInfo = await checkBalance({ addr, dashApi }); - //console.info(balanceInfo); - return; -} - -/** - * @param {Object} opts - * @param {any} opts.dashApi - TODO - * @param {String} opts.defaultAddr - * @param {Boolean} opts.forceConfirm - * @param {String} opts.insightBaseUrl - * @param {any} opts.insightApi - * @param {Array} args - */ -// ex: node ./bin/crowdnode.js transfer ./priv.wif 'pub' 0.01 -async function transferBalance( - { dashApi, defaultAddr, forceConfirm, insightBaseUrl, insightApi }, - args, -) { - let wif = await mustGetWif({ defaultAddr }, args); - - let keyname = args.shift() || ""; - let newAddr = await wifFileToAddr(keyname); - let dashAmount = parseFloat(args.shift() || "0"); - let duffAmount = Math.round(dashAmount * DUFFS); - let tx; - if (duffAmount) { - tx = await dashApi.createPayment(wif, newAddr, duffAmount); - } else { - tx = await dashApi.createBalanceTransfer(wif, newAddr); - } - if (duffAmount) { - let dashAmountStr = toDash(duffAmount); - console.info( - `Transferring ${duffAmount} (Đ${dashAmountStr}) to ${newAddr}...`, - ); - } else { - console.info(`Transferring balance to ${newAddr}...`); - } - await insightApi.instantSend(tx); - console.info(`Queued...`); - setTimeout(function () { - // TODO take a cleaner approach - // (waitForVout needs a reasonable timeout) - console.error(`Error: Transfer did not complete.`); - if (forceConfirm) { - console.error(`(using --unconfirmed may lead to rejected double spends)`); - } - process.exit(1); - }, 30 * 1000); - await Ws.waitForVout(insightBaseUrl, newAddr, 0); - console.info(`Accepted!`); - return; -} - -<<<<<<< Updated upstream - // State 1: not initialized, what does the user want? - if (needsInit) { - for (;;) { - let no; - if (!_force) { - no = await Prompt.prompt( - "Would you like to encrypt your keys with a passphrase? [Y/n]: ", - ); - } -======= -/** - * @param {Object} opts - * @param {String} opts.addr - * @param {any} opts.dashApi - TODO - */ -async function checkBalance({ addr, dashApi }) { - // deposit if balance is over 100,000 (0.00100000) - console.info("Checking balance... "); - let balanceInfo = await dashApi.getInstantBalance(addr); - let balanceDASH = toDASH(balanceInfo.balanceSat); ->>>>>>> Stashed changes - - let crowdNodeBalance = await CrowdNode.http.GetBalance(addr); - if (!crowdNodeBalance.TotalBalance) { - crowdNodeBalance.TotalBalance = 0; - crowdNodeBalance.TotalDividend = 0; - } - - let crowdNodeDuffNum = toDuff(crowdNodeBalance.TotalBalance); - let crowdNodeDASH = toDASH(crowdNodeDuffNum); - - let crowdNodeDivNum = toDuff(crowdNodeBalance.TotalDividend); - let crowdNodeDASHDiv = toDASH(crowdNodeDivNum); - - console.info(`Key: ${balanceDASH}`); - console.info(`CrowdNode: ${crowdNodeDASH}`); - console.info(`Dividends: ${crowdNodeDASHDiv}`); - console.info(); - /* - let balanceInfo = await insightApi.getBalance(pub); - if (balanceInfo.unconfirmedBalanceSat || balanceInfo.unconfirmedAppearances) { - if (!forceConfirm) { - console.error( - `Error: This address has pending transactions. Please try again in 1-2 minutes or use --unconfirmed.`, - ); - console.error(balanceInfo); - if ("status" !== subcommand) { - process.exit(1); - return; - } - } - } - */ - return balanceInfo; -} - -/** - * @param {Object} opts - * @param {String} opts.defaultAddr - * @param {Array} args - * @returns {Promise<[String, String]>} - */ -async function mustGetAddr({ defaultAddr }, args) { - let name = args.shift() ?? ""; - if (34 === name.length) { - // looks like addr already - // TODO make function for addr-lookin' check - return [name, name]; - } - - let addr = await maybeReadKeyPaths(name, { wif: false }); - if (addr) { - if (34 === addr.length) { - return [addr, name]; - } - //let pk = new Dashcore.PrivateKey(wif); - //let addr = pk.toAddress().toString(); - return [addr, name]; - } - - let isNum = !isNaN(parseFloat(name)); - if (isNum) { - args.unshift(name); - name = ""; - } - - if (name) { - console.error(); - console.error(`could not read '${name}' in ./ or match in ${keysDirRel}/.`); - console.error(); - process.exit(1); - return ["", name]; - } - - addr = await mustGetDefaultWif(defaultAddr, { wif: false }); - - // TODO we don't need defaultAddr, right? because it could be old? - return [addr, addr]; -} - -/** - * @param {Object} opts - * @param {String} opts.defaultAddr - * @param {Array} args - */ -async function mustGetWif({ defaultAddr }, args) { - let name = args.shift() ?? ""; - - let wif = await maybeReadKeyPaths(name, { wif: true }); - if (wif) { - return wif; - } - - let isNum = !isNaN(parseFloat(name)); - if (isNum) { - args.unshift(name); - name = ""; - } - - if (name) { - console.error(); - console.error( - `'${name}' does not match a staking key in ./ or ${keysDirRel}/`, - ); - console.error(); - process.exit(1); - return ""; - } - - wif = await mustGetDefaultWif(defaultAddr); - - return wif; -} - -/** - * @param {Object} opts - * @param {String} opts.defaultAddr - */ -async function initKeystore({ defaultAddr }) { - // if we have no keys, make one - let wifnames = await listManagedKeynames(); - if (!wifnames.length) { - return await generateKey({ defaultKey: defaultAddr }, []); - } - // if we have no passphrase, ask about it - await initPassphrase(); - return defaultAddr || wifnames[0]; -} - -/** - * @param {String} name - * @param {Object} opts - * @param {Boolean} opts.wif - * @returns {Promise} - wif - */ -async function maybeReadKeyPaths(name, opts) { - let privKey = ""; - - // prefix match in .../keys/ - let wifname = await findWif(name); - if (!wifname) { - return ""; - } - - if (false === opts.wif) { - return wifname.slice(0, -".wif".length); - } - - let filepath = Path.join(keysDir, wifname); - privKey = await maybeReadKeyFile(filepath); - if (!privKey) { - // local in ./ - privKey = await maybeReadKeyFile(name); - } - - return privKey; -} - -// Subcommands - -/** - * @param {Object} psuedoState - * @param {String} psuedoState.defaultKey - addr name of default key - * @param {Boolean} [psuedoState.plainText] - don't encrypt - * @param {Array} args - */ -async function generateKey({ defaultKey, plainText }, args) { - let name = args.shift(); - //@ts-ignore - TODO submit JSDoc PR for Dashcore - let pk = new Dashcore.PrivateKey(); - - let addr = pk.toAddress().toString(); - let plainWif = pk.toWIF(); - - let wif = plainWif; - if (!plainText) { - wif = await maybeEncrypt(plainWif); - } - - let filename = `~/${configdir}/keys/${addr}.wif`; - let filepath = Path.join(`${keysDir}/${addr}.wif`); - let note = ""; - if (name) { - filename = name; - filepath = name; - note = `\n(for pubkey address ${addr})`; - let err = await Fs.access(filepath).catch(Object); - if (!err) { - // TODO - console.info(`'${filepath}' already exists (will not overwrite)`); - process.exit(0); - return; - } - } - - await Fs.writeFile(filepath, wif, "utf8"); - if (!name && !defaultKey) { - await Fs.writeFile(defaultWifPath, addr, "utf8"); - } - - console.info(``); - console.info(`Generated ${filename} ${note}`); - console.info(``); - return addr; -} - -async function initPassphrase() { - let needsInit = false; - let shadow = await Fs.readFile(shadowPath, "utf8").catch( - emptyStringOnErrEnoent, - ); - if (!shadow) { - needsInit = true; - } - if (needsInit) { - await cmds.getPassphrase({}, []); - } -} - -/** - * @param {Object} state - * @param {Boolean} [state._askPreviousPassphrase] - don't ask for passphrase again - * @param {Array} args - */ -async function setPassphrase({ _askPreviousPassphrase }, args) { - let result = { - passphrase: "", - changed: false, - }; - let date = getFsDateString(); - - // get the old passphrase - if (false !== _askPreviousPassphrase) { - // TODO should contain the shadow? - await cmds.getPassphrase({ _rotatePassphrase: true }, []); - } - - // get the new passphrase - let newPassphrase = await promptPassphrase(); - let curShadow = await Fs.readFile(shadowPath, "utf8").catch( - emptyStringOnErrEnoent, - ); - - let newShadow = await Cipher.shadowPassphrase(newPassphrase); - await Fs.writeFile(shadowPath, newShadow, "utf8"); - - let rawKeys = await readAllKeys(); - let encAddrs = rawKeys - .map(function (raw) { - if (raw.encrypted) { - return raw.addr; - } - }) - .filter(Boolean); - - // backup all currently encrypted files - //@ts-ignore - if (encAddrs.length) { - let filepath = Path.join(HOME, `${configdir}/keys.${date}.bak`); - console.info(``); - console.info(`Backing up previous (encrypted) keys:`); - encAddrs.unshift(`SHADOW:${curShadow}`); - await Fs.writeFile(filepath, encAddrs.join("\n") + "\n", "utf8"); - console.info(` ~/${configdir}/keys.${date}.bak`); - console.info(``); - } - cmds._setPassphrase(newPassphrase); - - await encryptAll(rawKeys, { rotateKey: true }); - - result.passphrase = newPassphrase; - result.changed = true; - return result; -} - -async function promptPassphrase() { - let newPassphrase; - for (;;) { - newPassphrase = await Prompt.prompt("Enter (new) passphrase: ", { - mask: true, - }); - newPassphrase = newPassphrase.trim(); - - let _newPassphrase = await Prompt.prompt("Enter passphrase again: ", { - mask: true, - }); - _newPassphrase = _newPassphrase.trim(); - - let match = Cipher.secureCompare(newPassphrase, _newPassphrase); - if (match) { - break; - } - - console.error("passphrases do not match"); - } - return newPassphrase; -} - -/** - * Import and Encrypt - * @param {Object} opts - * @param {String} opts.keypath - */ -async function importKey({ keypath }) { - let key = await maybeReadKeyFileRaw(keypath); - if (!key?.wif) { - console.error(`no key found for '${keypath}'`); - process.exit(1); - return; - } - - let encWif = await maybeEncrypt(key.wif); - let icon = "💾"; - if (encWif.includes(":")) { - icon = "🔐"; - } - let date = getFsDateString(); - - await safeSave( - Path.join(keysDir, `${key.addr}.wif`), - encWif, - Path.join(keysDir, `${key.addr}.${date}.bak`), - ); - - console.info(`${icon} Imported ${keysDirRel}/${key.addr}.wif`); - console.info(``); - - return key.addr; -} - -/** - * @param {Object} opts - * @param {Boolean} [opts._rotatePassphrase] - * @param {Boolean} [opts._force] - * @param {Array} args - */ -cmds.getPassphrase = async function ({ _rotatePassphrase, _force }, args) { - let result = { - passphrase: "", - changed: false, - }; - /* - if (!_rotatePassphrase) { - let cachedphrase = cmds._getPassphrase(); - if (cachedphrase) { - return cachedphrase; - } - } - */ - - // Three possible states: - // 1. no shadow file yet (ask to set one) - // 2. empty shadow file (initialized, but not set - don't ask to set one) - // 3. encrypted shadow file (initialized, requires passphrase) - let needsInit = false; - let shadow = await Fs.readFile(shadowPath, "utf8").catch( - emptyStringOnErrEnoent, - ); - if (!shadow) { - needsInit = true; - } else if (NO_SHADOW === shadow && _force) { - needsInit = true; - } - - // State 1: not initialized, what does the user want? - if (needsInit) { - for (;;) { - let no; - if (!_force) { - no = await Prompt.prompt( - "Would you like to set an encryption passphrase? [Y/n]: ", - ); - } - - // Set a passphrase and create shadow file - if (!no || ["yes", "y"].includes(no.toLowerCase())) { - result = await setPassphrase({ _askPreviousPassphrase: false }, args); - cmds._setPassphrase(result.passphrase); - return result; - } - - // ask user again - if (!["no", "n"].includes(no.toLowerCase())) { - continue; - } - - // No passphrase, create a NONE shadow file - await Fs.writeFile(shadowPath, NO_SHADOW, "utf8"); - return result; - } -<<<<<<< Updated upstream - let crowdNodeDuffNum = toDuff(crowdNodeBalance.TotalBalance); - let crowdNodeDASH = toDASH(crowdNodeDuffNum); - - let crowdNodeDivNum = toDuff(crowdNodeBalance.TotalDividend); - let crowdNodeDivDASH = toDASH(crowdNodeDivNum); - process.stdout.write( - ` ${balanceDASH} | ${crowdNodeDASH} | ${crowdNodeDivDASH} |`, - ); - - totals.key += balanceInfo.balanceSat; - totals.dividend += crowdNodeBalance.TotalDividend; - totals.stake += crowdNodeBalance.TotalBalance; - - console.info(); - }, Promise.resolve()); - console.info( - `| | | | |`, - ); - let total = `| Totals`; - totals.keyDash = toDASH(toDuff(totals.key.toString())); - totals.stakeDash = toDASH(toDuff(totals.stake.toString())); - totals.dividendDash = toDASH(toDuff(totals.dividend.toString())); - console.info( - `${total} | ${totals.keyDash} | ${totals.stakeDash} | ${totals.dividendDash} |`, - ); - debug(``); - - if (warns.length) { - console.warn(`Warnings:`); - warns.forEach(function (warn) { - console.warn(`${warn.node}: ${warn.error.message}`); - }); - console.warn(``); - } -} - -/** - * @param {String} name - ex: Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.wif.enc - */ -function isNamedLikeKey(name) { - // TODO distinguish with .enc extension? - let hasGoodLength = 34 + 4 === name.length || 34 + 4 + 4 === name.length; - let knownExt = name.endsWith(".wif") || name.endsWith(".wif.enc"); - let isTmp = name.startsWith(".") || name.startsWith("_"); - return hasGoodLength && knownExt && !isTmp; -} - -/** - * @param {Object} opts - * @param {any} opts.dashApi - TODO - * @param {String} opts.addr - * @param {String} opts.filepath - * @param {String} opts.insightBaseUrl - * @param {Array} args - */ -async function removeKey({ addr, dashApi, filepath, insightBaseUrl }, args) { - let balanceInfo = await dashApi.getInstantBalance(addr); - - let balanceDash = toDash(balanceInfo.balanceSat); - if (balanceInfo.balanceSat) { - console.error(``); - console.error(`Error: ${addr}`); - console.error( - ` still has a balance of ${balanceInfo.balanceSat} (Đ${balanceDash})`, - ); - console.error(` (transfer to another address before deleting)`); - console.error(``); - process.exit(1); - return; - } - - await initCrowdNode(insightBaseUrl); - let crowdNodeBalance = await CrowdNode.http.GetBalance(addr); - if (!crowdNodeBalance) { - // may be janky if not registered - crowdNodeBalance = {}; - } - if (!crowdNodeBalance.TotalBalance) { - crowdNodeBalance.TotalBalance = 0; - } - let crowdNodeDash = toDash(crowdNodeBalance.TotalBalance); - if (crowdNodeBalance.TotalBalance) { - console.error(``); - console.error(`Error: ${addr}`); - console.error( - ` still staking ${crowdNodeBalance.TotalBalance} (Đ${crowdNodeDash}) on CrowdNode`, - ); - console.error( - ` (withdraw 100.0 and transfer to another address before deleting)`, - ); - console.error(``); - process.exit(1); - return; - } - - let wifname = await findWif(addr); - let fullpath = Path.join(keysDir, wifname); - let wif = await maybeReadKeyPaths(filepath, { wif: true }); - - await Fs.unlink(fullpath).catch(function (err) { - console.error(`could not remove ${filepath}: ${err.message}`); - process.exit(1); - }); - - let wifnames = await listManagedKeynames(); - console.info(``); - console.info(`No balances found. Removing ${filepath}.`); - console.info(``); - console.info(`Backup (just in case):`); - console.info(` ${wif}`); - console.info(``); - if (!wifnames.length) { - console.info(`No keys left.`); - console.info(``); - } else { - let newAddr = wifnames[0]; - debug(`Selected ${newAddr} as new default staking key.`); - await Fs.writeFile(defaultWifPath, addr.replace(".wif", ""), "utf8"); - console.info(``); -======= ->>>>>>> Stashed changes - } - - // State 2: shadow already initialized to empty - // (user doesn't want a passphrase) - if (!shadow) { - cmds._setPassphrase(""); - return result; - } - - // State 3: passphrase & shadow already in use - for (;;) { - let prompt = `Enter passphrase: `; - if (_rotatePassphrase) { - prompt = `Enter (current) passphrase: `; - } - result.passphrase = await Prompt.prompt(prompt, { - mask: true, - }); - result.passphrase = result.passphrase.trim(); - if (!result.passphrase || "q" === result.passphrase) { - console.error("cancel: no passphrase"); - process.exit(1); - return result; - } - - let match = await Cipher.checkPassphrase(result.passphrase, shadow); - if (match) { - cmds._setPassphrase(result.passphrase); - console.info(``); - return result; - } - - console.error("incorrect passphrase"); - } - - throw new Error("SANITY FAIL: unreachable return"); -}; - -cmds._getPassphrase = function () { - return ""; -}; - -/** - * @param {String} passphrase - */ -cmds._setPassphrase = function (passphrase) { - // Look Ma! A private variable! - cmds._getPassphrase = function () { - return passphrase; - }; -}; - -/** - * Encrypt ALL-the-things! - * @param {Object} [opts] - * @param {Boolean} opts.rotateKey - * @param {Array?} rawKeys - */ -async function encryptAll(rawKeys, opts) { - if (!rawKeys) { - rawKeys = await readAllKeys(); - } - let date = getFsDateString(); - - let passphrase = cmds._getPassphrase(); - if (!passphrase) { - let result = await cmds.getPassphrase({ _force: true }, []); - if (result.changed) { - // encryptAll was already called on rotation - return; - } - passphrase = result.passphrase; - } - - console.info(`Encrypting...`); - console.info(``); - await rawKeys.reduce(async function (promise, key) { - await promise; - - if (key.encrypted && !opts?.rotateKey) { - console.info(`🙈 ${key.addr} [already encrypted]`); - return; - } - let encWif = await maybeEncrypt(key.wif, { force: true }); - await safeSave( - Path.join(keysDir, `${key.addr}.wif`), - encWif, - Path.join(keysDir, `${key.addr}.${date}.bak`), - ); - console.info(`🔑 ${key.addr}`); - }, Promise.resolve()); - console.info(``); - console.info(`Done 🔐`); - console.info(``); -} - -/** - * @param {String} encWif - */ -async function decrypt(encWif) { - let passphrase = cmds._getPassphrase(); - if (!passphrase) { - let result = await cmds.getPassphrase({}, []); - passphrase = result.passphrase; - // we don't return just in case they're setting a passphrase to - // decrypt a previously encrypted file (i.e. for recovery from elsewhere) - } - let key128 = await Cipher.deriveKey(passphrase); - let cipher = Cipher.create(key128); - - return cipher.decrypt(encWif); -} - -/** - * Decrypt ALL-the-things! - * @param {Array?} rawKeys - */ -async function decryptAll(rawKeys) { - if (!rawKeys) { - rawKeys = await readAllKeys(); - } - let date = getFsDateString(); - - console.info(``); - console.info(`Decrypting...`); - console.info(``); -<<<<<<< Updated upstream - console.info( - `Send Đ${effectiveDash} to your staking key via the QR above, or its address:`, - ); - console.info(`${addr}`); - console.info( - `(this key will be used to fund and control your CrowdNode account)`, - ); -======= - await rawKeys.reduce(async function (promise, key) { - await promise; - - if (!key.encrypted) { - console.info(`📖 ${key.addr} [already decrypted]`); - return; - } - await safeSave( - Path.join(keysDir, `${key.addr}.wif`), - key.wif, - Path.join(keysDir, `${key.addr}.${date}.bak`), - ); - console.info(`🔓 ${key.addr}`); - }, Promise.resolve()); ->>>>>>> Stashed changes - console.info(``); - console.info(`Done ${DONE}`); - console.info(``); -} - -function getFsDateString() { - // YYYY-MM-DD_hh-mm_ss - let date = new Date() - .toISOString() - .replace(/:/g, ".") - .replace(/T/, "_") - .replace(/\.\d{3}.*/, ""); - return date; -} - -/** - * @param {String} filepath - * @param {String} wif - * @param {String} bakpath - */ -async function safeSave(filepath, wif, bakpath) { - let tmpPath = `${bakpath}.tmp`; - await Fs.writeFile(tmpPath, wif, "utf8"); - let err = await Fs.access(filepath).catch(Object); - if (!err) { - await Fs.rename(filepath, bakpath); - } - await Fs.rename(tmpPath, filepath); - if (!err) { - await Fs.unlink(bakpath); - } -} - -/** - * @typedef {Object} RawKey - * @property {String} addr - * @property {Boolean} encrypted - * @property {String} wif - */ -<<<<<<< Updated upstream -// ex: node ./bin/crowdnode.js transfer Xxxxx 'pub' 0.01 -async function transferBalance( - { dashApi, defaultAddr, forceConfirm, insightBaseUrl, insightApi }, - args, -) { - /** @type Array */ - let getAddrArgs = []; - - // There are two cases in which we could have only 2 arguments, - // and the first argument could be an address inside or outside - // of the wallet. - // - // Ex: - // crowdnode transfer {source} {dest} - // crowdnode transfer {source} {dest} {amount} - // crowdnode transfer {dest} {amount} - // crowdnode transfer {dest} - // - // To disambiguate, we check if the second argument is an amount. - if (3 === args.length) { - getAddrArgs = args; - } else if (2 === args.length) { - let maybeAmount = parseFloat(args[1]); - let isAddr = isNaN(maybeAmount); - if (isAddr) { - getAddrArgs = args; - } - } - let wif = await mustGetWif({ defaultAddr }, getAddrArgs); -======= ->>>>>>> Stashed changes - -/** - * @throws - */ -async function readAllKeys() { - let wifnames = await listManagedKeynames(); - - /** @type Array */ - let keys = []; - await wifnames.reduce(async function (promise, wifname) { - await promise; - - let keypath = Path.join(keysDir, wifname); - let key = await maybeReadKeyFileRaw(keypath); - if (!key?.wif) { - return; - } - - if (`${key.addr}.wif` !== wifname) { - throw new Error( - `computed pubkey '${key.addr}' of WIF does not match filename '${keypath}'`, - ); - } - - keys.push(key); - }, Promise.resolve()); - - return keys; -} - -/** - * @param {String} filepath - * @param {Object} [opts] - * @param {Boolean} opts.wif - * @returns {Promise} - */ -async function maybeReadKeyFile(filepath, opts) { - let key = await maybeReadKeyFileRaw(filepath, opts); - if (false === opts?.wif) { - return key?.addr || ""; - } - return key?.wif || ""; -} - -/** - * @param {String} filepath - * @param {Object} [opts] - * @param {Boolean} opts.wif - * @returns {Promise} - */ -<<<<<<< Updated upstream -async function sendSignup({ dashApi, defaultAddr, insightBaseUrl }, args) { - let [addr, name] = await mustGetAddr({ defaultAddr }, args); - await initCrowdNode(insightBaseUrl); - let hotwallet = CrowdNode.main.hotwallet; - let state = await getCrowdNodeStatus({ addr, hotwallet }); - let balanceInfo = await dashApi.getInstantBalance(addr); - - if (state.status?.signup) { - console.info(`${addr} is already signed up. Here's the account status:`); - console.info(` ${state.signup} SignUpForApi`); - console.info(` ${state.accept} AcceptTerms`); - console.info(` ${state.deposit} DepositReceived`); - return; -======= -async function maybeReadKeyFileRaw(filepath, opts) { - let privKey = await Fs.readFile(filepath, "utf8").catch( - emptyStringOnErrEnoent, - ); - privKey = privKey.trim(); - if (!privKey) { - return null; ->>>>>>> Stashed changes - } - - let encrypted = false; - if (privKey.includes(":")) { - encrypted = true; - try { - if (false !== opts?.wif) { - privKey = await decrypt(privKey); - } - } catch (err) { - //@ts-ignore - console.error(err.message); - console.error(`passphrase does not match for key ${filepath}`); - process.exit(1); - } - } - if (false === opts?.wif) { - return { - addr: Path.basename(filepath, ".wif"), - encrypted: encrypted, - wif: "", - }; - } - - let pk = new Dashcore.PrivateKey(privKey); - let pub = pk.toAddress().toString(); - - return { - addr: pub, - encrypted: encrypted, - wif: privKey, - }; -} - -// tuple example {Promise<[String, Boolean]>} -/** - * @param {Object} [opts] - * @param {Boolean} [opts.force] - * @param {String} plainWif - */ -async function maybeEncrypt(plainWif, opts) { - let passphrase = cmds._getPassphrase(); - if (!passphrase) { - let result = await cmds.getPassphrase({}, []); - passphrase = result.passphrase; - } - if (!passphrase) { - if (opts?.force) { - throw new Error(`no passphrase with which to encrypt file`); - } - return plainWif; - } - - let key128 = await Cipher.deriveKey(passphrase); - let cipher = Cipher.create(key128); - return cipher.encrypt(plainWif); -} - -// TODO option to specify config dir - -/** - * @param {Object} opts - * @param {any} opts.dashApi - TODO - * @param {String} opts.defaultAddr - * @param {Array} args - */ -async function listKeys({ dashApi, defaultAddr }, args) { - let wifnames = await listManagedKeynames(); - - if (wifnames) { - // to print 'default staking key' message - await mustGetAddr({ defaultAddr }, args); - } - - /** - * @type Array<{ node: String, error: Error }> - */ - let warns = []; - // console.error because console.debug goes to stdout, not stderr - debug(``); - debug(`Staking keys: (in ${keysDirRel}/)`); - debug(``); - - await wifnames.reduce(async function (promise, wifname) { - await promise; - - let wifpath = Path.join(keysDir, wifname); - let addr = await maybeReadKeyFile(wifpath, { wif: false }).catch(function ( - err, - ) { - warns.push({ node: wifname, error: err }); - return ""; - }); - if (!addr) { - return; - } - - console.info(`${addr}`); - }, Promise.resolve()); - debug(``); - - if (warns.length) { - console.warn(`Warnings:`); - warns.forEach(function (warn) { - console.warn(`${warn.node}: ${warn.error.message}`); - }); - console.warn(``); - } -} - -/** - * @param {String} name - ex: Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.wif.enc - */ -function isNamedLikeKey(name) { - // TODO distinguish with .enc extension? - let hasGoodLength = 34 + 4 === name.length || 34 + 4 + 4 === name.length; - let knownExt = name.endsWith(".wif") || name.endsWith(".wif.enc"); - let isTmp = name.startsWith(".") || name.startsWith("_"); - return hasGoodLength && knownExt && !isTmp; -} - -/** - * @param {Object} opts - * @param {any} opts.dashApi - TODO - * @param {String} opts.addr - * @param {String} opts.filepath - * @param {String} opts.insightBaseUrl - * @param {Array} args - */ -async function removeKey({ addr, dashApi, filepath, insightBaseUrl }, args) { - let balanceInfo = await dashApi.getInstantBalance(addr); - - let balanceDash = toDash(balanceInfo.balanceSat); - if (balanceInfo.balanceSat) { - console.error(``); - console.error(`Error: ${addr}`); - console.error( - ` still has a balance of ${balanceInfo.balanceSat} (Đ${balanceDash})`, - ); - console.error(` (transfer to another address before deleting)`); - console.error(``); - process.exit(1); - return; - } - - await initCrowdNode(insightBaseUrl); - let crowdNodeBalance = await CrowdNode.http.GetBalance(addr); - if (!crowdNodeBalance) { - // may be janky if not registered - crowdNodeBalance = {}; - } - if (!crowdNodeBalance.TotalBalance) { - crowdNodeBalance.TotalBalance = 0; - } - let crowdNodeDash = toDash(crowdNodeBalance.TotalBalance); - if (crowdNodeBalance.TotalBalance) { - console.error(``); - console.error(`Error: ${addr}`); - console.error( - ` still staking ${crowdNodeBalance.TotalBalance} (Đ${crowdNodeDash}) on CrowdNode`, - ); - console.error( - ` (withdrawal 100.0 and transfer to another address before deleting)`, - ); - console.error(``); - process.exit(1); - return; - } - -<<<<<<< Updated upstream - // this would allow for at least 2 withdraws costing (21000 + 1000) - let reserve = 50000; - let reserveDash = toDash(reserve); - if (!noReserve) { - console.info( - `reserving ${reserve} (Đ${reserveDash}) for withdraws (--no-reserve to disable)`, - ); -======= - let wifname = await findWif(addr); - let fullpath = Path.join(keysDir, wifname); - let wif = await maybeReadKeyPaths(filepath, { wif: true }); - - await Fs.unlink(fullpath).catch(function (err) { - console.error(`could not remove ${filepath}: ${err.message}`); - process.exit(1); - }); - - let wifnames = await listManagedKeynames(); - console.info(``); - console.info(`No balances found. Removing ${filepath}.`); - console.info(``); - console.info(`Backup (just in case):`); - console.info(` ${wif}`); - console.info(``); - if (!wifnames.length) { - console.info(`No keys left.`); - console.info(``); ->>>>>>> Stashed changes - } else { - let newAddr = wifnames[0]; - debug(`Selected ${newAddr} as new default staking key.`); - await Fs.writeFile(defaultWifPath, addr.replace(".wif", ""), "utf8"); - console.info(``); - } -} - -/** - * @param {String} pre - */ -async function findWif(pre) { - if (!pre) { - return ""; - } - - let names = await listManagedKeynames(); - names = names.filter(function (name) { - return name.startsWith(pre); - }); - - if (!names.length) { - return ""; - } - - if (names.length > 1) { - console.error(`'${pre}' is ambiguous:`, names.join(", ")); - process.exit(1); - return ""; - } - - return names[0]; -} - -async function listManagedKeynames() { - let nodes = await Fs.readdir(keysDir); - - return nodes.filter(isNamedLikeKey); -} - -/** - * @param {Object} opts - * @param {String} opts.defaultAddr - * @param {String} opts.insightBaseUrl - * @param {Array} args - */ -<<<<<<< Updated upstream -async function withdrawDash({ dashApi, defaultAddr, insightBaseUrl }, args) { -======= -async function loadAddr({ defaultAddr, insightBaseUrl }, args) { ->>>>>>> Stashed changes - let [addr] = await mustGetAddr({ defaultAddr }, args); - -<<<<<<< Updated upstream - if (!state.status?.accept) { - console.error(`no account for address ${addr}`); - process.exit(1); - return; - } - - let percentStr = args.shift() || "100.0"; - // pass: .1 0.1, 1, 1.0, 10, 10.0, 100, 100.0 - // fail: 1000, 10.00 - if (!/^1?\d?\d?(\.\d)?$/.test(percentStr)) { - console.error("Error: withdraw percent must be between 0.1 and 100.0"); - process.exit(1); - } - let percent = parseFloat(percentStr); - - let permil = Math.round(percent * 10); - if (permil <= 0 || permil > 1000) { - console.error("Error: withdraw percent must be between 0.1 and 100.0"); - process.exit(1); - } - - let realPercentStr = (permil / 10).toFixed(1); - console.info(`Initiating withdraw of ${realPercentStr}%...`); - - let wifname = await findWif(addr); - let filepath = Path.join(keysDir, wifname); - let wif = await maybeReadKeyFile(filepath); - let paid = await CrowdNode.withdraw(wif, hotwallet, permil); - //let paidFloat = (paid.satoshis / DUFFS).toFixed(8); - //let paidInt = paid.satoshis.toString().padStart(9, "0"); - console.info(`API Response: ${paid.api}`); -======= - let desiredAmountDash = parseFloat(args.shift() || "0"); - let desiredAmountDuff = Math.round(desiredAmountDash * DUFFS); - - let effectiveDuff = desiredAmountDuff; - let effectiveDash = ""; - if (!effectiveDuff) { - effectiveDuff = CrowdNode.stakeMinimum + signupTotal + feeEstimate; - effectiveDuff = roundDuff(effectiveDuff, 3); - effectiveDash = toDash(effectiveDuff); - } - - await plainLoadAddr({ addr, effectiveDash, effectiveDuff, insightBaseUrl }); - ->>>>>>> Stashed changes - return; -} - -/** - * @param {Object} opts - * @param {String} opts.addr - * @param {String} opts.effectiveDash - * @param {Number} opts.effectiveDuff - * @param {String} opts.insightBaseUrl - */ -async function plainLoadAddr({ - addr, - effectiveDash, - effectiveDuff, - insightBaseUrl, -}) { - console.info(``); - showQr(addr, effectiveDuff); - console.info(``); - console.info( - `Use the QR Code above to load ${effectiveDuff} (Đ${effectiveDash}) onto your staking key.`, - ); - console.info(``); - console.info(`(waiting...)`); - console.info(``); - let payment = await Ws.waitForVout(insightBaseUrl, addr, 0); - console.info(`Received ${payment.satoshis}`); -} - -// Helpers - -/** - * Convert prefix, addr, keyname, or filepath to pub addr - * @param {String} name - * @throws - */ -async function wifFileToAddr(name) { - if (34 === name.length) { - // actually payment addr - return name; - } - - let privKey = ""; - - let wifname = await findWif(name); - if (wifname) { - let filepath = Path.join(keysDir, wifname); - privKey = await maybeReadKeyFile(filepath); - } - if (!privKey) { - privKey = await maybeReadKeyFile(name); - } - if (!privKey) { - throw new Error("bad file path or address"); - } - - let pk = new Dashcore.PrivateKey(privKey); - let pub = pk.toPublicKey().toAddress().toString(); - return pub; -} - -/** - * @param {Error & { code: String }} err - * @throws - */ -function emptyStringOnErrEnoent(err) { - if ("ENOENT" !== err.code) { - throw err; - } - return ""; -} - -/** - * @param {Number} duffs - ex: 00000000 - */ -function toDash(duffs) { - return (duffs / DUFFS).toFixed(8); -} - -/** - * @param {Number} duffs - ex: 00000000 - */ -function toDASH(duffs) { - let dash = (duffs / DUFFS).toFixed(8); - return `Đ` + dash.padStart(12, " "); -} - -/** - * @param {String} dash - ex: 0.00000000 - */ -function toDuff(dash) { - return Math.round(parseFloat(dash) * DUFFS); -} - -/** - * 1000 to Round to the nearest mDash - * ex: 0.50238108 => 0.50300000 - * @param {Number} effectiveDuff - * @param {Number} numDigits - */ -function roundDuff(effectiveDuff, numDigits) { - let n = Math.pow(10, numDigits); - let effectiveDash = toDash(effectiveDuff); - effectiveDuff = toDuff( - (Math.ceil(parseFloat(effectiveDash) * n) / n).toString(), - ); - return effectiveDuff; -} - -/** - * @param {Array} arr - * @param {any} item - */ -function removeItem(arr, item) { - let index = arr.indexOf(item); - if (index >= 0) { - return arr.splice(index, 1)[0]; - } - return null; -} - -// Run - -main().catch(function (err) { - console.error("Fail:"); - console.error(err.stack || err); - process.exit(1); -}); diff --git a/cli b/cli index 949d278..ef8a233 160000 --- a/cli +++ b/cli @@ -1 +1 @@ -Subproject commit 949d278e580d7f43b703e988e9edbe34fdf441b6 +Subproject commit ef8a233106b074893ea569525cf3aaf4a987dc61 diff --git a/crowdnode.js b/crowdnode.js index 65b1d0f..b2bc8cd 100644 --- a/crowdnode.js +++ b/crowdnode.js @@ -9,16 +9,21 @@ let CrowdNode = {}; exports.CrowdNode = CrowdNode; - const DUFFS = 100000000; + const SATOSHIS = 100000000; let Dash = exports.DashApi || require("./dashapi.js"); - let Dashcore = exports.dashcore || require("./lib/dashcore.js"); - let Insight = exports.DashSight || require("dashsight"); + let Dashcore = exports.dashcore || require("./dashcore-lit.js"); + let DashSight = exports.DashSight || require("dashsight"); let Ws = exports.DashSocket || require("dashsight/ws"); - CrowdNode._insightBaseUrl = ""; + CrowdNode._initialized = false; + CrowdNode._dashsocketBaseUrl = ""; // TODO don't require these shims - CrowdNode._insightApi = Insight.create({ baseUrl: "" }); + CrowdNode._insightApi = DashSight.create({ + dashsightBaseUrl: "", + dashsocketBaseUrl: "", + insightBaseUrl: "", + }); CrowdNode._dashApi = Dash.create({ insightApi: CrowdNode._insightApi }); CrowdNode.main = { @@ -40,8 +45,9 @@ CrowdNode.offset = 20000; CrowdNode.duffs = 100000000; + CrowdNode.satoshis = 100000000; CrowdNode.depositMinimum = 100000; - CrowdNode.stakeMinimum = toDuff(0.5); + CrowdNode.stakeMinimum = toSatoshis(0.5); /** * @type {Record} @@ -83,19 +89,38 @@ /** * @param {Object} opts * @param {String} opts.baseUrl - * @param {String} opts.insightBaseUrl + * @param {String} opts.dashsightBaseUrl - typically ends with /insight-api + * @param {String} opts.dashsocketBaseUrl - typically ends with /socket.io + * @param {String} opts.insightBaseUrl - typically ends with /insight-api */ - CrowdNode.init = async function ({ baseUrl, insightBaseUrl }) { + CrowdNode.init = async function ({ + baseUrl, + dashsightBaseUrl, + dashsocketBaseUrl, + insightBaseUrl, + }) { // TODO use API // See https://github.com/dashhive/crowdnode.js/issues/3 CrowdNode._baseUrl = baseUrl; - CrowdNode._insightBaseUrl = insightBaseUrl; - CrowdNode._insightApi = Insight.create({ - baseUrl: insightBaseUrl, + if ("https://insight.dash.org" === insightBaseUrl) { + insightBaseUrl = "https://insight.dash.org/insight-api"; + if (!dashsocketBaseUrl) { + dashsocketBaseUrl = "https://insight.dash.org/socket.io"; + } + if (!dashsightBaseUrl) { + dashsightBaseUrl = "https://dashsight.dashincubator.dev/insight-api"; + } + } + CrowdNode._dashsocketBaseUrl = dashsocketBaseUrl; + CrowdNode._insightApi = DashSight.create({ + dashsightBaseUrl: dashsightBaseUrl, + dashsocketBaseUrl: dashsocketBaseUrl, + insightBaseUrl: insightBaseUrl, }); CrowdNode._dashApi = Dash.create({ insightApi: CrowdNode._insightApi }); + CrowdNode._initialized = true; }; /** @@ -127,8 +152,8 @@ if (vout.scriptPubKey.addresses[0] !== signupAddr) { return; } - let amount = Math.round(parseFloat(vout.value) * DUFFS); - let msg = amount - CrowdNode.offset; + let sats = Math.round(parseFloat(vout.value) * SATOSHIS); + let msg = sats - CrowdNode.offset; if (CrowdNode.responses.DepositReceived === msg) { status.deposit = tx.time; @@ -165,7 +190,7 @@ // Send Request Message let pk = new Dashcore.PrivateKey(wif); let msg = CrowdNode.offset + CrowdNode.requests.signupForApi; - let changeAddr = pk.toPublicKey().toAddress().toString(); + let changeAddr = (await pk.toPublicKey().toAddress()).toString(); let tx = await CrowdNode._dashApi.createPayment( wif, hotwallet, @@ -175,7 +200,11 @@ await CrowdNode._insightApi.instantSend(tx.serialize()); let reply = CrowdNode.offset + CrowdNode.responses.PleaseAcceptTerms; - return await Ws.waitForVout(CrowdNode._insightBaseUrl, changeAddr, reply); + return await Ws.waitForVout( + CrowdNode._dashsocketBaseUrl, + changeAddr, + reply, + ); }; /** @@ -186,7 +215,7 @@ // Send Request Message let pk = new Dashcore.PrivateKey(wif); let msg = CrowdNode.offset + CrowdNode.requests.acceptTerms; - let changeAddr = pk.toPublicKey().toAddress().toString(); + let changeAddr = (await pk.toPublicKey().toAddress()).toString(); let tx = await CrowdNode._dashApi.createPayment( wif, hotwallet, @@ -197,31 +226,35 @@ let reply = CrowdNode.offset + CrowdNode.responses.WelcomeToCrowdNodeBlockChainAPI; - return await Ws.waitForVout(CrowdNode._insightBaseUrl, changeAddr, reply); + return await Ws.waitForVout( + CrowdNode._dashsocketBaseUrl, + changeAddr, + reply, + ); }; /** * @param {String} wif * @param {String} hotwallet - * @param {Number} amount - Duffs (1/100000000 Dash) + * @param {Number} satoshis - base unit of 1/100000000 Dash */ - CrowdNode.deposit = async function (wif, hotwallet, amount) { + CrowdNode.deposit = async function (wif, hotwallet, satoshis) { // Send Request Message let pk = new Dashcore.PrivateKey(wif); - let changeAddr = pk.toPublicKey().toAddress().toString(); + let changeAddr = (await pk.toPublicKey().toAddress()).toString(); // TODO reserve a balance let tx; - if (amount) { - if (amount < CrowdNode.depositMinimum) { + if (satoshis) { + if (satoshis < CrowdNode.depositMinimum) { throw new Error( - `cannot deposit '${amount}': less than minimum of '${CrowdNode.depositMinimum}'`, + `cannot deposit '${satoshis}': less than minimum of '${CrowdNode.depositMinimum}'`, ); } tx = await CrowdNode._dashApi.createPayment( wif, hotwallet, - amount, + satoshis, changeAddr, ); } else { @@ -230,7 +263,11 @@ await CrowdNode._insightApi.instantSend(tx.serialize()); let reply = CrowdNode.offset + CrowdNode.responses.DepositReceived; - return await Ws.waitForVout(CrowdNode._insightBaseUrl, changeAddr, reply); + return await Ws.waitForVout( + CrowdNode._dashsocketBaseUrl, + changeAddr, + reply, + ); }; /** @@ -248,7 +285,7 @@ // Send Request Message let pk = new Dashcore.PrivateKey(wif); let msg = CrowdNode.offset + permil; - let changeAddr = pk.toPublicKey().toAddress().toString(); + let changeAddr = (await pk.toPublicKey().toAddress()).toString(); let tx = await CrowdNode._dashApi.createPayment( wif, hotwallet, @@ -266,7 +303,7 @@ satoshis: 0, txlock: false, }; - return await Ws.listen(CrowdNode._insightBaseUrl, findResponse); + return await Ws.listen(CrowdNode._dashsocketBaseUrl, findResponse); /** * @param {String} evname @@ -293,12 +330,12 @@ return false; } - let duffs = vout[addr]; - let msg = duffs - CrowdNode.offset; + let sats = vout[addr]; + let msg = sats - CrowdNode.offset; let api = CrowdNode._responses[msg]; if (!api) { // the withdraw often happens before the queued message - console.warn(` => received '${duffs}' (${evname})`); + console.warn(` => received '${sats}' (${evname})`); return false; } @@ -307,7 +344,7 @@ api: api.toString(), at: now, txid: data.txid, - satoshis: duffs, + satoshis: sats, txlock: data.txlock, }; @@ -399,7 +436,7 @@ } // Workaround for https://github.com/dashhive/crowdnode-cli/issues/19 - // (we could also `b = Math.round(Math.floor(b * DUFFS) / DUFFS)`) + // (we could also `b = Math.round(Math.floor(b * SATOSHIS) / SATOSHIS)`) balanceInfo.TotalBalance = parseFloat(balanceInfo.TotalBalance.toFixed(8)); balanceInfo.TotalActiveBalance = parseFloat( balanceInfo.TotalActiveBalance.toFixed(8), @@ -497,10 +534,10 @@ /** * @param {String|Number} dash */ - function toDuff(dash) { + function toSatoshis(dash) { //@ts-ignore let dashF = parseFloat(dash); - return Math.round(dashF * DUFFS); + return Math.round(dashF * SATOSHIS); } if ("undefined" !== typeof module) { diff --git a/dashapi.js b/dashapi.js index e247f9b..0505b44 100644 --- a/dashapi.js +++ b/dashapi.js @@ -5,13 +5,13 @@ //@ts-ignore exports.DashApi = Dash; - const DUFFS = 100000000; - const DUST = 10000; + const SATOSHIS = 100000000; const FEE = 1000; //@ts-ignore - let Dashcore = exports.dashcore || require("./lib/dashcore.js"); + let Dashcore = exports.dashcore || require("./dashcore-lit.js"); let Transaction = Dashcore.Transaction; + let PrivateKey = Dashcore.PrivateKey; Dash.create = function ({ //@ts-ignore TODO @@ -32,7 +32,7 @@ }, 0); // because 0.1 + 0.2 = 0.30000000000000004, // but we would only want 0.30000000 - let floatBalance = parseFloat((balance / DUFFS).toFixed(8)); + let floatBalance = parseFloat((balance / SATOSHIS).toFixed(8)); return { addrStr: address, @@ -51,8 +51,8 @@ * @param {String} pub */ dashApi.createBalanceTransfer = async function (privKey, pub) { - let pk = new Dashcore.PrivateKey(privKey); - let changeAddr = pk.toPublicKey().toAddress().toString(); + let pk = new PrivateKey(privKey); + let changeAddr = (await pk.toPublicKey().toAddress()).toString(); let body = await insightApi.getUtxos(changeAddr); let utxos = await getUtxos(body); @@ -65,7 +65,7 @@ //@ts-ignore - allows single value or array .from(utxos); tmpTx.to(pub, balance - 1000); - tmpTx.sign(pk); + await tmpTx.sign(pk); // TODO getsmartfeeestimate?? // fee = 1duff/byte (2 chars hex is 1 byte) @@ -78,7 +78,7 @@ .from(utxos); tx.to(pub, balance - fee); tx.fee(fee); - tx.sign(pk); + await tx.sign(pk); return tx; }; @@ -86,25 +86,25 @@ /** * Send with change back * @param {String} privKey - * @param {(String|import('@dashevo/dashcore-lib').Address)} payAddr - * @param {Number} amount - * @param {(String|import('@dashevo/dashcore-lib').Address)} [changeAddr] + * @param {String} payAddr + * @param {Number} satoshis - base unit of DASH (a.k.a. "duffs") + * @param {String} [changeAddr] */ dashApi.createPayment = async function ( privKey, payAddr, - amount, + satoshis, changeAddr, ) { - let pk = new Dashcore.PrivateKey(privKey); - let utxoAddr = pk.toPublicKey().toAddress().toString(); + let pk = new PrivateKey(privKey); + let utxoAddr = (await pk.toPublicKey().toAddress()).toString(); if (!changeAddr) { changeAddr = utxoAddr; } // TODO make more accurate? let feePreEstimate = 1000; - let utxos = await getOptimalUtxos(utxoAddr, amount + feePreEstimate); + let utxos = await getOptimalUtxos(utxoAddr, satoshis + feePreEstimate); let balance = getBalance(utxos); if (!utxos.length) { @@ -112,18 +112,18 @@ } // (estimate) don't send dust back as change - if (balance - amount <= DUST + FEE) { - amount = balance; + if (balance - satoshis <= Transaction.DUST_AMOUNT + FEE) { + satoshis = balance; } //@ts-ignore - no input required, actually let tmpTx = new Transaction() //@ts-ignore - allows single value or array .from(utxos); - tmpTx.to(payAddr, amount); + tmpTx.to(payAddr, satoshis); //@ts-ignore - the JSDoc is wrong in dashcore-lib/lib/transaction/transaction.js tmpTx.change(changeAddr); - tmpTx.sign(pk); + await tmpTx.sign(pk); // TODO getsmartfeeestimate?? // fee = 1duff/byte (2 chars hex is 1 byte) @@ -132,19 +132,19 @@ let fee = 10 + tmpTx.toString().length / 2; // (adjusted) don't send dust back as change - if (balance + -amount + -fee <= DUST) { - amount = balance - fee; + if (balance + -satoshis + -fee <= Transaction.DUST_AMOUNT) { + satoshis = balance - fee; } //@ts-ignore - no input required, actually let tx = new Transaction() //@ts-ignore - allows single value or array .from(utxos); - tx.to(payAddr, amount); + tx.to(payAddr, satoshis); tx.fee(fee); //@ts-ignore - see above tx.change(changeAddr); - tx.sign(pk); + await tx.sign(pk); return tx; }; @@ -152,16 +152,16 @@ // TODO make more optimal /** * @param {String} utxoAddr - * @param {Number} fullAmount - including fee estimate + * @param {Number} totalSatoshis - including fee estimate */ - async function getOptimalUtxos(utxoAddr, fullAmount) { + async function getOptimalUtxos(utxoAddr, totalSatoshis) { // get smallest coin larger than transaction // if that would create dust, donate it as tx fee let body = await insightApi.getUtxos(utxoAddr); let utxos = await getUtxos(body); let balance = getBalance(utxos); - if (balance < fullAmount) { + if (balance < totalSatoshis) { return []; } @@ -176,7 +176,7 @@ // try to get just one utxos.every(function (utxo) { - if (utxo.satoshis > fullAmount) { + if (utxo.satoshis > totalSatoshis) { included[0] = utxo; total = utxo.satoshis; return true; @@ -191,7 +191,7 @@ utxos.some(function (utxo) { included.push(utxo); total += utxo.satoshis; - return total >= fullAmount; + return total >= totalSatoshis; }); return included; } @@ -207,6 +207,7 @@ /** * @param {Array} body + * @returns {Promise>} */ async function getUtxos(body) { /** @type Array */ @@ -232,7 +233,7 @@ return false; } - let satoshis = Math.round(parseFloat(vout.value) * DUFFS); + let satoshis = Math.round(parseFloat(vout.value) * SATOSHIS); if (utxo.satoshis !== satoshis) { return false; } @@ -243,6 +244,7 @@ data.vout.some(findAndSetUtxoIndex); + // TODO test without txid utxos.push({ txId: utxo.txid, outputIndex: utxoIndex, diff --git a/dashcore-lit.js b/dashcore-lit.js new file mode 100644 index 0000000..8a291ac --- /dev/null +++ b/dashcore-lit.js @@ -0,0 +1,489 @@ +(function (exports) { + "use strict"; + + let Dashcore = {}; + //@ts-ignore + exports.dashcore = Dashcore; + + let Base58Check = require("@dashincubator/base58check").Base58Check; + //@ts-ignore + let BlockTx = exports.BlockTx || require("@dashincubator/blocktx"); + //@ts-ignore + let Crypto = exports.crypto || require("./shims/crypto-node.js"); + let RIPEMD160 = require("@dashincubator/ripemd160"); + let Secp256k1 = require("@dashincubator/secp256k1"); + + let b58c = Base58Check.create({ + pubKeyHashVersion: "4c", + privateKeyVersion: "cc", + }); + + let dashTx = BlockTx.create({ + version: 3, + /** + * @param {String} addr + * @returns {String} + */ + addrToPubKeyHash: function (addr) { + let b58c = Base58Check.create({ + pubKeyHashVersion: "4c", + privateKeyVersion: "cc", + }); + + // XXX bad idea? + // using .decode to avoid the async of .verify + let parts = b58c.decode(addr); + return parts.pubKeyHash; + }, + }); + + function Transaction() { + //@ts-ignore + return Dashcore.Transaction.create.apply(null, arguments); + } + Dashcore.Transaction = Transaction; + + // 193 + BlockTx.OUTPUT_SIZE; + Transaction.DUST_AMOUNT = 5460; + + Transaction.create = function () { + let changeAddr = ""; + let fee = 0; + let txInfo = { + /** @type {Array} */ + inputs: [], + locktime: 0, + /** @type {Array} */ + outputs: [], + version: 3, + }; + /** @type {import('@dashincubator/blocktx').TxInfoSigned} */ + let txSigned; + + let coreTx = {}; + + /** + * @param {Array} utxos + */ + coreTx.from = function (utxos) { + if (!Array.isArray(utxos)) { + utxos = [utxos]; + } + txInfo.inputs = txInfo.inputs.concat(utxos); + return coreTx; + }; + + /** + * @param {Array} payments + * @param {Number} satoshis - base unit of DASH (a.k.a. "duffs") + */ + coreTx.to = function (payments, satoshis) { + if (!Array.isArray(payments)) { + payments = [ + { + address: payments, + satoshis: satoshis, + }, + ]; + } + txInfo.outputs = txInfo.outputs.concat(payments); + return coreTx; + }; + + /** + * @param {Number} targetFee + */ + coreTx.fee = function (targetFee) { + fee = targetFee; + + return coreTx; + }; + + /** + * @param {String} address + */ + coreTx.change = function (address) { + changeAddr = address; + + return coreTx; + }; + + /** + * @param {Array} keys + */ + coreTx.sign = async function (keys) { + if (!Array.isArray(keys)) { + keys = [keys]; + } + + let _txInfo = Object.assign({}, txInfo); + let changeOutput = await coreTx._calculateFee(); + if (changeOutput) { + _txInfo.outputs.push(changeOutput); + } + let _keys = await coreTx._mapKeysToUtxos(_txInfo, keys); + console.log(_keys.length, _txInfo.inputs.length); + txSigned = await dashTx.hashAndSignAll(_txInfo, _keys); + }; + + coreTx._calculateFee = async function () { + let sats = txInfo.inputs.reduce(function (total, utxo) { + return total + utxo.satoshis; + }, 0); + let paid = txInfo.outputs.reduce(function (total, payment) { + return total + payment.satoshis; + }, 0); + + let [minFee, maxFee] = BlockTx.estimates(txInfo); + let feeSpread = maxFee - minFee; + let halfSpread = Math.ceil(feeSpread / 2); + if (!fee) { + fee = maxFee; + } + if (fee < minFee) { + throw new Error( + `your fees are too powerful: increase fee to at least ${minFee} (absolute possible minimum) + ${halfSpread} (to account for possible byte padding) + ${BlockTx.OUTPUT_SIZE} (if you expect change back)`, + ); + } + + let myMaxFee = Math.max(fee, maxFee); + let change = sats + -paid + -myMaxFee; + if (change <= Transaction.DUST_AMOUNT) { + change = 0; + } + + let changeFee = 0; + /** @type {import('@dashincubator/blocktx').TxOutput?} */ + let changeOutput = null; + if (change) { + if (!changeAddr) { + let bigFee = fee + change; + throw new Error( + `you must provide 'change(addr)' to collect '${change}' sats or increase 'fee' to '${bigFee}'`, + ); + } + changeFee = BlockTx.OUTPUT_SIZE; + change -= changeFee; + changeOutput = { + address: changeAddr, + satoshis: change, + }; + } + + let total = paid + myMaxFee; + if (total > sats) { + let debt = sats - total; + throw new Error( + `your spending is too powerful: ${debt} = ${sats} (inputs) + -${paid} (outputs) + -${myMaxFee} (fee) + -${changeFee} (change fee)`, + ); + } + + return changeOutput; + }; + + /** + * @param {import('@dashincubator/blocktx').TxInfo} _txInfo + * @param {Array} keys + */ + coreTx._mapKeysToUtxos = async function (_txInfo, keys) { + /** @type {Array} */ + let utxoKeys = []; + /** @type {Object.} */ + let keysMap = {}; + for (let key of keys) { + let privInst = PrivateKeyFactory.create(key); + let privBuf = privInst._toUint8Array(); + let pubInst = await privInst.toPublicKey(); + let addrInst = await pubInst.toAddress(); + console.log("DEBUG", addrInst._address, addrInst._pubKeyHash); + keysMap[addrInst._address] = privBuf; + keysMap[addrInst._pubKeyHash] = privBuf; + } + for (let input of _txInfo.inputs) { + let index = input.address || input.pubKeyHash || ""; + let privBuf = keysMap[index]; + if (!privBuf) { + let outStr = JSON.stringify(input); + throw new Error(`missing private key for input: ${outStr}`); + } + utxoKeys.push(privBuf); + } + + return utxoKeys; + }; + + coreTx.toString = function () { + // TODO produce raw tx if not signed + return txSigned.transaction; + }; + + coreTx.serialize = function () { + // TODO run checks on fees and change and stuff + return txSigned.transaction; + }; + + return coreTx; + }; + + /** + * @typedef Address + * @prop {String} _address + * @prop {String} _pubKeyHash + * @prop {ToString} toString + */ + + /** + * @typedef PrivateKey + * @prop {ToAddress} toAddress + * @prop {ToPublicKey} toPublicKey + * @prop {ToUint8Array} _toUint8Array + */ + + /** + * @typedef {String|Uint8Array|ArrayBuffer|Buffer|PrivateKey} PrivateKeyish + */ + + /** + * @typedef PublicKey + * @prop {ToAddress} toAddress + * @prop {ToUint8Array} _toUint8Array + */ + + /** + * @callback ToAddress + * @returns {Promise
} - Payment Address (Public) instance object + */ + + /** + * @callback ToPublicKey + * @returns {PublicKey} + */ + + /** + * @callback ToString + * @returns {String} - payAddr (Public) + */ + + /** + * @callback ToUint8Array + * @returns {Uint8Array} - key bytes + */ + + /** + * @callback ToWIF + * @returns {Promise} - wif (Private) + */ + + /** + * @param {String|Uint8Array|ArrayBuffer|Buffer} wifHexOrBuf + * @returns {PrivateKey} + */ + function PrivateKeyFactory(wifHexOrBuf) { + return PrivateKeyFactory.create(wifHexOrBuf); + } + Dashcore.PrivateKey = PrivateKeyFactory; + + /** + * @param {String|Uint8Array|ArrayBuffer|Buffer} wifHexOrBuf + * @returns {PrivateKey} + */ + PrivateKeyFactory.create = function (wifHexOrBuf) { + let pk = {}; + /** @type {Uint8Array} */ + let privBuf; + if (!wifHexOrBuf) { + privBuf = Secp256k1.utils.randomPrivateKey(); + } + privBuf = PrivateKeyFactory.from(wifHexOrBuf); + + /** @type ToWIF */ + pk.toWIF = async function () { + let wif = await privateKeyToWif(privBuf); + return wif; + }; + + /** @type ToAddress */ + pk.toAddress = async function () { + return await pk.toPublicKey().toAddress(); + }; + + pk.toPublicKey = function () { + let pubBuf = BlockTx.utils.toPublicKey(privBuf); + let pub = PublicKeyFactory.create(pubBuf); + return pub; + }; + + pk._toUint8Array = function () { + return privBuf; + }; + + return pk; + }; + + /** + * @param {any} pk + * @returns {pk is PrivateKey} + */ + PrivateKeyFactory.isPrivateKey = function (pk) { + return "function" === typeof pk._toUint8Array; + }; + + /** + * @param {PrivateKeyish} wifHexOrBuf + * @returns {Uint8Array} + */ + PrivateKeyFactory.from = function (wifHexOrBuf) { + let isPrivateKeyInst = PrivateKeyFactory.isPrivateKey(wifHexOrBuf); + if (isPrivateKeyInst) { + //@ts-ignore + return wifHexOrBuf._toUint8Array(); + } + + if ("string" === typeof wifHexOrBuf) { + if (64 === wifHexOrBuf.length) { + return BlockTx.utils.hexToU8(wifHexOrBuf); + } + if (52 === wifHexOrBuf.length) { + return wifToPrivateKey(wifHexOrBuf); + } + throw new Error( + "cannot create private key from non-hex, non-wif strings", + ); + } + + //@ts-ignore + if (wifHexOrBuf?.buffer) { + //@ts-ignore + return new Uint8Array(wifHexOrBuf.buffer); + } + //@ts-ignore + if (wifHexOrBuf.byteLength) { + //@ts-ignore + return new Uint8Array(wifHexOrBuf); + } + throw new Error( + "cannot create private key from non-string, non-buffer type", + ); + }; + + /** + * @param {String|Uint8Array|ArrayBuffer|Buffer} hexOrBuf + * @returns {PublicKey} + */ + function PublicKeyFactory(hexOrBuf) { + return PublicKeyFactory.create(hexOrBuf); + } + Dashcore.PublicKey = PublicKeyFactory; + + /** + * @param {String|Uint8Array|ArrayBuffer|Buffer} hexOrBuf + * @returns {PublicKey} + */ + PublicKeyFactory.create = function (hexOrBuf) { + let pubBuf = PublicKeyFactory.from(hexOrBuf); + + let pub = {}; + pub._pubKeyHash = ""; + pub._address = ""; + pub._buffer = pubBuf; + + /** @type ToAddress */ + pub.toAddress = async function () { + pub._pubKeyHash = await hashPublicKey(pubBuf); + pub._address = await pubKeyHashToAddr(pub._pubKeyHash); + return { + _pubKeyHash: pub._pubKeyHash, + _address: pub._address, + toString: function () { + return pub._address; + }, + }; + }; + pub._toUint8Array = function () { + return pub._buffer; + }; + + return pub; + }; + + /** + * @param {String|Uint8Array|ArrayBuffer|Buffer} hexOrBuf + * @returns {Uint8Array} + */ + PublicKeyFactory.from = function (hexOrBuf) { + if ("string" === typeof hexOrBuf) { + if (64 === hexOrBuf.length) { + return BlockTx.utils.hexToU8(hexOrBuf); + } + throw new Error("cannot create public key from non-hex strings"); + } + + //@ts-ignore + if (hexOrBuf?.buffer) { + //@ts-ignore + return new Uint8Array(hexOrBuf.buffer); + } + if (hexOrBuf.byteLength) { + return new Uint8Array(hexOrBuf); + } + throw new Error( + "cannot create public key from non-string, non-buffer type", + ); + }; + + /** + * @param {Uint8Array} pubBuf + * @return {Promise} - hex sha256 ripemd160 pubKeyHash + */ + async function hashPublicKey(pubBuf) { + let sha = await Crypto.subtle.digest("SHA-256", pubBuf); + let shaU8 = new Uint8Array(sha); + let ripemd = RIPEMD160.create(); + let hash = ripemd.update(shaU8); + let pkh = hash.digest("hex"); + // extra .toString() for tsc + return pkh.toString(); + } + + /** + * @param {Uint8Array} privBuf - Private Key as Uint8Array + * @returns {Promise} wif - Base58Check-encoded private key + */ + async function privateKeyToWif(privBuf) { + let privHex = BlockTx.utils.u8ToHex(privBuf); + let decoded = { + privateKey: privHex, + }; + + let wif = await b58c.encode(decoded); + return wif; + } + + /** + * @param {String} pubKeyHash - pkh hex + * @returns {Promise} addr - Base58Check-encoded payment address + */ + async function pubKeyHashToAddr(pubKeyHash) { + let addr = await b58c.encode({ + version: "4c", + pubKeyHash: pubKeyHash, + }); + return addr; + } + + /** + * @param {String} wif + * @returns {Uint8Array} + */ + function wifToPrivateKey(wif) { + //let parts = await b58c.verify(wif); + // TODO verifySync + let parts = b58c.decode(wif); + let privBuf = Buffer.from(parts.privateKey, "hex"); + return privBuf; + } + + if ("undefined" !== typeof module) { + module.exports = Dashcore; + } +})(("undefined" !== typeof module && module.exports) || window); diff --git a/lib/browser-dashcore.js b/lib/browser-dashcore.js deleted file mode 100644 index eb176c4..0000000 --- a/lib/browser-dashcore.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; - -module.exports = require("@dashevo/dashcore-lib/dist/dashcore-lib.min.js"); diff --git a/lib/dashcore.js b/lib/dashcore.js deleted file mode 100644 index ef7e9e7..0000000 --- a/lib/dashcore.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; - -module.exports = require("@dashevo/dashcore-lib"); diff --git a/package-lock.json b/package-lock.json index 1345182..7f787bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,291 +1,68 @@ { "name": "crowdnode", - "version": "1.7.0", + "version": "1.8.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "crowdnode", - "version": "1.7.0", + "version": "1.8.0", "license": "SEE LICENSE IN LICENSE", "dependencies": { - "@dashevo/dashcore-lib": "^0.19.41", - "dashsight": "^1.1.1" + "@dashincubator/base58check": "^1.3.1", + "@dashincubator/blocktx": "^0.9.0-2", + "@dashincubator/ripemd160": "^2.3.0", + "@dashincubator/secp256k1": "^1.7.1-1", + "dashsight": "^1.3.0-0" }, "bin": { "crowdnode": "bin/crowdnode.js" }, - "devDependencies": {}, "optionalDependencies": { - "@root/request": "^1.9.1", + "@root/request": "^1.9.2", "dotenv": "^16.0.1", "qrcode-svg": "^1.1.0", "tough-cookie": "^4.0.0", "ws": "^8.8.0" } }, - "node_modules/@dashevo/dashcore-lib": { - "version": "0.19.41", - "resolved": "https://registry.npmjs.org/@dashevo/dashcore-lib/-/dashcore-lib-0.19.41.tgz", - "integrity": "sha512-xsbDcV9P3WOa1s4X6pVbLXKD7xuuB5YqsW1GjE1iyeN+hXXKUJzky5txKt8e9ZEQF7Qd/L/enWupcdHRYhgsqg==", - "dependencies": { - "@dashevo/x11-hash-js": "^1.0.2", - "@types/node": "^12.12.47", - "bloom-filter": "^0.2.0", - "bls-signatures": "^0.2.5", - "bn.js": "^4.12.0", - "bs58": "=4.0.1", - "elliptic": "^6.5.4", - "eslint-config-prettier": "^8.3.0", - "inherits": "=2.0.1", - "lodash": "^4.17.20", - "ripemd160": "^2.0.2", - "unorm": "^1.6.0" - } - }, - "node_modules/@dashevo/x11-hash-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@dashevo/x11-hash-js/-/x11-hash-js-1.0.2.tgz", - "integrity": "sha512-3vvnZweBca4URBXHF+FTrM4sdTpp3IMt73G1zUKQEdYm/kJkIKN94qpFai7YZDl87k64RCH+ckRZk6ruQPz5KQ==" - }, - "node_modules/@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", - "peer": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "peer": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "peer": true - }, - "node_modules/@root/request": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@root/request/-/request-1.9.1.tgz", - "integrity": "sha512-ROVgS5qgTvXDCI9XoJawJCB+QfC9jF7t3pFWmdUibjHqPt+2wV7pYSgth65qb65bK3qDIv9xXYF5FBECKhe8Iw==" - }, - "node_modules/@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - }, - "node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "peer": true, + "node_modules/@dashincubator/base58check": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@dashincubator/base58check/-/base58check-1.3.1.tgz", + "integrity": "sha512-v2vYOwsTmbfaADJUg+0Vsw61/P5XtdDnjZtImDIC5DePIA/2lT9V+00nR5GsKfwc40WQ02tFlN+xoFt9cx+P7A==", "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peer": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "peer": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "peer": true - }, - "node_modules/base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "dependencies": { - "safe-buffer": "^5.0.1" + "base58check": "bin/base58check.js" } }, - "node_modules/bloom-filter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bloom-filter/-/bloom-filter-0.2.0.tgz", - "integrity": "sha512-RMG2RpnKczVzRsEYSPaT5rKsyj0w5/wpQRjaW4vOMe1WyUDQpoqxjNc10uROEjdhu63ytRt6aFRPXFePi/Rd7A==" - }, - "node_modules/bls-signatures": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/bls-signatures/-/bls-signatures-0.2.5.tgz", - "integrity": "sha512-5TzQNCtR4zWE4lM08EOMIT8l3b4h8g5LNKu50fUYP1PnupaLGSLklAcTto4lnH7VXpyhsar+74L9wNJII4E/4Q==" - }, - "node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" - }, - "node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "dependencies": { - "base-x": "^3.0.2" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "node_modules/@dashincubator/blocktx": { + "version": "0.9.0-2", + "resolved": "https://registry.npmjs.org/@dashincubator/blocktx/-/blocktx-0.9.0-2.tgz", + "integrity": "sha512-g6IQxx/nuwsl3Dxzq61bE/UM8t/qQOnnnpF6BzPReHFd4XudQvQ+WcMXRJbrCQWj4yoR5SOCCBZ6IG7As3aVOg==", + "bin": { + "blocktx-inspect": "bin/inspect.js" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "node_modules/@dashincubator/ripemd160": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@dashincubator/ripemd160/-/ripemd160-2.3.0.tgz", + "integrity": "sha512-SfaUhvXti0Ut6ZhlAwurrRAG56phxqWozj72Hixx/v3LSQj1/wjkgTuDaexil672CzLD+RRWoPgp41BSM+DJJA==" }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "peer": true + "node_modules/@dashincubator/secp256k1": { + "version": "1.7.1-1", + "resolved": "https://registry.npmjs.org/@dashincubator/secp256k1/-/secp256k1-1.7.1-1.tgz", + "integrity": "sha512-zkCfHPiIBWkSHojMEVBGNi9y9vX0NHGxkwO5zdZVlhbiqpWGOPbFRbUIwbEFdlnx4tS2x8w8InNzl36qVYWsGg==" }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "peer": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } + "node_modules/@root/request": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@root/request/-/request-1.9.2.tgz", + "integrity": "sha512-wVaL9yVV9oDR9UNbPZa20qgY+4Ch6YN8JUkaE4el/uuS5dmhD8Lusm/ku8qJVNtmQA56XLzEDCRS6/vfpiHK2A==" }, "node_modules/dashsight": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/dashsight/-/dashsight-1.1.1.tgz", - "integrity": "sha512-fcNGAxbFlqh2p3Al0Jq+byexPK0HrQMYGzpvgINVs1DI+bQLgI1tjgbpNJf8l7uu5LVmDBq8tjVKoH+hCSzPPA==", + "version": "1.3.0-0", + "resolved": "https://registry.npmjs.org/dashsight/-/dashsight-1.3.0-0.tgz", + "integrity": "sha512-KR2Vj6Ts94FLRFr0A3xqa4PAf3R9njrN5k7IHzbzDZjohb+cG3+11e48W0Dvt6CQGC4+yToRnUvlrBmGdpU5TQ==", "dependencies": { - "@root/request": "^1.9.1" + "@root/request": "^1.9.2" }, "bin": { "dashsight-balance": "bin/balance.js", @@ -298,41 +75,6 @@ "dotenv": "^16.0.1" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "peer": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "peer": true - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "peer": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dotenv": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", @@ -342,587 +84,6 @@ "node": ">=12" } }, - "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/elliptic/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.17.0.tgz", - "integrity": "sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==", - "peer": true, - "dependencies": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "peer": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", - "peer": true, - "dependencies": { - "acorn": "^8.7.1", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "peer": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "peer": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "peer": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "peer": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "peer": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "peer": true - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "peer": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "peer": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "peer": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "peer": true - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "peer": true - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "peer": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", - "peer": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/hash-base/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hash.js/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "peer": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "peer": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "peer": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "peer": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==" - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "peer": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "peer": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "peer": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "peer": true - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "peer": true - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "peer": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "peer": true - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "peer": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "peer": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "peer": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "peer": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -933,171 +94,19 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/qrcode-svg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/qrcode-svg/-/qrcode-svg-1.1.0.tgz", - "integrity": "sha512-XyQCIXux1zEIA3NPb0AeR8UMYvXZzWEhgdBgBjH9gO7M48H9uoHzviNz8pXw3UzrAcxRRRn9gxHewAVK7bn9qw==", "optional": true, - "bin": { - "qrcode-svg": "bin/qrcode-svg.js" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readable-stream/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "peer": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "peer": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "peer": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "peer": true + "node_modules/qrcode-svg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/qrcode-svg/-/qrcode-svg-1.1.0.tgz", + "integrity": "sha512-XyQCIXux1zEIA3NPb0AeR8UMYvXZzWEhgdBgBjH9gO7M48H9uoHzviNz8pXw3UzrAcxRRRn9gxHewAVK7bn9qw==", + "optional": true, + "bin": { + "qrcode-svg": "bin/qrcode-svg.js" + } }, "node_modules/tough-cookie": { "version": "4.0.0", @@ -1113,30 +122,6 @@ "node": ">=6" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -1146,64 +131,6 @@ "node": ">= 4.0.0" } }, - "node_modules/unorm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", - "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "peer": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "peer": true - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "peer": true - }, "node_modules/ws": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz", @@ -1227,726 +154,46 @@ } }, "dependencies": { - "@dashevo/dashcore-lib": { - "version": "0.19.41", - "resolved": "https://registry.npmjs.org/@dashevo/dashcore-lib/-/dashcore-lib-0.19.41.tgz", - "integrity": "sha512-xsbDcV9P3WOa1s4X6pVbLXKD7xuuB5YqsW1GjE1iyeN+hXXKUJzky5txKt8e9ZEQF7Qd/L/enWupcdHRYhgsqg==", - "requires": { - "@dashevo/x11-hash-js": "^1.0.2", - "@types/node": "^12.12.47", - "bloom-filter": "^0.2.0", - "bls-signatures": "^0.2.5", - "bn.js": "^4.12.0", - "bs58": "=4.0.1", - "elliptic": "^6.5.4", - "eslint-config-prettier": "^8.3.0", - "inherits": "=2.0.1", - "lodash": "^4.17.20", - "ripemd160": "^2.0.2", - "unorm": "^1.6.0" - } - }, - "@dashevo/x11-hash-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@dashevo/x11-hash-js/-/x11-hash-js-1.0.2.tgz", - "integrity": "sha512-3vvnZweBca4URBXHF+FTrM4sdTpp3IMt73G1zUKQEdYm/kJkIKN94qpFai7YZDl87k64RCH+ckRZk6ruQPz5KQ==" + "@dashincubator/base58check": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@dashincubator/base58check/-/base58check-1.3.1.tgz", + "integrity": "sha512-v2vYOwsTmbfaADJUg+0Vsw61/P5XtdDnjZtImDIC5DePIA/2lT9V+00nR5GsKfwc40WQ02tFlN+xoFt9cx+P7A==" }, - "@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", - "peer": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } + "@dashincubator/blocktx": { + "version": "0.9.0-2", + "resolved": "https://registry.npmjs.org/@dashincubator/blocktx/-/blocktx-0.9.0-2.tgz", + "integrity": "sha512-g6IQxx/nuwsl3Dxzq61bE/UM8t/qQOnnnpF6BzPReHFd4XudQvQ+WcMXRJbrCQWj4yoR5SOCCBZ6IG7As3aVOg==" }, - "@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "peer": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } + "@dashincubator/ripemd160": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@dashincubator/ripemd160/-/ripemd160-2.3.0.tgz", + "integrity": "sha512-SfaUhvXti0Ut6ZhlAwurrRAG56phxqWozj72Hixx/v3LSQj1/wjkgTuDaexil672CzLD+RRWoPgp41BSM+DJJA==" }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "peer": true + "@dashincubator/secp256k1": { + "version": "1.7.1-1", + "resolved": "https://registry.npmjs.org/@dashincubator/secp256k1/-/secp256k1-1.7.1-1.tgz", + "integrity": "sha512-zkCfHPiIBWkSHojMEVBGNi9y9vX0NHGxkwO5zdZVlhbiqpWGOPbFRbUIwbEFdlnx4tS2x8w8InNzl36qVYWsGg==" }, "@root/request": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@root/request/-/request-1.9.1.tgz", - "integrity": "sha512-ROVgS5qgTvXDCI9XoJawJCB+QfC9jF7t3pFWmdUibjHqPt+2wV7pYSgth65qb65bK3qDIv9xXYF5FBECKhe8Iw==" - }, - "@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - }, - "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "peer": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peer": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "peer": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "peer": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "peer": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "peer": true - }, - "base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "bloom-filter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bloom-filter/-/bloom-filter-0.2.0.tgz", - "integrity": "sha512-RMG2RpnKczVzRsEYSPaT5rKsyj0w5/wpQRjaW4vOMe1WyUDQpoqxjNc10uROEjdhu63ytRt6aFRPXFePi/Rd7A==" - }, - "bls-signatures": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/bls-signatures/-/bls-signatures-0.2.5.tgz", - "integrity": "sha512-5TzQNCtR4zWE4lM08EOMIT8l3b4h8g5LNKu50fUYP1PnupaLGSLklAcTto4lnH7VXpyhsar+74L9wNJII4E/4Q==" - }, - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "peer": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "requires": { - "base-x": "^3.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "peer": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "peer": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "peer": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@root/request/-/request-1.9.2.tgz", + "integrity": "sha512-wVaL9yVV9oDR9UNbPZa20qgY+4Ch6YN8JUkaE4el/uuS5dmhD8Lusm/ku8qJVNtmQA56XLzEDCRS6/vfpiHK2A==" }, "dashsight": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/dashsight/-/dashsight-1.1.1.tgz", - "integrity": "sha512-fcNGAxbFlqh2p3Al0Jq+byexPK0HrQMYGzpvgINVs1DI+bQLgI1tjgbpNJf8l7uu5LVmDBq8tjVKoH+hCSzPPA==", + "version": "1.3.0-0", + "resolved": "https://registry.npmjs.org/dashsight/-/dashsight-1.3.0-0.tgz", + "integrity": "sha512-KR2Vj6Ts94FLRFr0A3xqa4PAf3R9njrN5k7IHzbzDZjohb+cG3+11e48W0Dvt6CQGC4+yToRnUvlrBmGdpU5TQ==", "requires": { - "@root/request": "^1.9.1", + "@root/request": "^1.9.2", "dotenv": "^16.0.1" } }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "peer": true, - "requires": { - "ms": "2.1.2" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "peer": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "peer": true, - "requires": { - "esutils": "^2.0.2" - } - }, "dotenv": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==", "optional": true }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - } - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "peer": true - }, - "eslint": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.17.0.tgz", - "integrity": "sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==", - "peer": true, - "requires": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - } - }, - "eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "requires": {} - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "peer": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "peer": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "peer": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "peer": true - }, - "espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", - "peer": true, - "requires": { - "acorn": "^8.7.1", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "peer": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "peer": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "peer": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "peer": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "peer": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "peer": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "peer": true - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "peer": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "peer": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "peer": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "peer": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "peer": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "peer": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "peer": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", - "peer": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true - }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - } - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - } - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "peer": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "peer": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "peer": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "peer": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "peer": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "peer": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "peer": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "peer": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "peer": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "peer": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "peer": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "peer": true - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "peer": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "peer": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "peer": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "peer": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "peer": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "peer": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "peer": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "peer": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "peer": true - }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -1956,7 +203,8 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "optional": true }, "qrcode-svg": { "version": "1.1.0", @@ -1964,111 +212,6 @@ "integrity": "sha512-XyQCIXux1zEIA3NPb0AeR8UMYvXZzWEhgdBgBjH9gO7M48H9uoHzviNz8pXw3UzrAcxRRRn9gxHewAVK7bn9qw==", "optional": true }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - } - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "peer": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "peer": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "peer": true, - "requires": { - "glob": "^7.1.3" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "peer": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "peer": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "peer": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "peer": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "peer": true - }, "tough-cookie": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", @@ -2080,73 +223,12 @@ "universalify": "^0.1.2" } }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "peer": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "peer": true - }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "optional": true }, - "unorm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", - "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==" - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "peer": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "peer": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "peer": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "peer": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "peer": true - }, "ws": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz", diff --git a/package.json b/package.json index 7146cde..b9c2a31 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,14 @@ { "name": "crowdnode", - "version": "1.7.0", + "version": "1.8.0", "description": "Manage your stake in Đash with the CrowdNode Blockchain API", "main": "./index.js", "browser": { - "./lib/request.js": "./lib/request-browser.js", - "./lib/dashcore.js": "./lib/browser-dashcore.js" + "index.js": "crowdnode.js", + "./lib/dashcore.js": "./lib/browser-dashcore.js", + "./lib/request.js": "./shims/request-browser.js", + "./shims/crypto-node.js": "./shims/crypto-browser.js", + "crypto": false }, "bin": { "crowdnode": "./bin/crowdnode.js" @@ -39,11 +42,14 @@ }, "homepage": "https://github.com/dashhive/crowdnode.js#readme", "dependencies": { - "@dashevo/dashcore-lib": "^0.19.41", - "dashsight": "^1.1.1" + "@dashincubator/base58check": "^1.3.1", + "@dashincubator/blocktx": "^0.9.0-2", + "@dashincubator/ripemd160": "^2.3.0", + "@dashincubator/secp256k1": "^1.7.1-1", + "dashsight": "^1.3.0-0" }, "optionalDependencies": { - "@root/request": "^1.9.1", + "@root/request": "^1.9.2", "dotenv": "^16.0.1", "qrcode-svg": "^1.1.0", "tough-cookie": "^4.0.0", diff --git a/shims/crypto-node.js b/shims/crypto-node.js new file mode 100644 index 0000000..c6d31e9 --- /dev/null +++ b/shims/crypto-node.js @@ -0,0 +1,3 @@ +"use strict"; + +module.exports = require("node:crypto"); diff --git a/lib/browser-request.js b/shims/request-browser.js similarity index 100% rename from lib/browser-request.js rename to shims/request-browser.js diff --git a/types.js b/types.js index 3e40fd8..db1c9ac 100644 --- a/types.js +++ b/types.js @@ -67,7 +67,7 @@ * @typedef {Object} InsightSocketEventData * @property {String} txid - hex * @property {Number} valueOut - float - * @property {Array>} vout - addr and duffs + * @property {Array>} vout - addr and satoshis * @property {Boolean} isRBF * @property {Boolean} txlock *