From a1a8e61020429c8c10860e5cb89029f2a324e821 Mon Sep 17 00:00:00 2001 From: Caleb Kniffen Date: Fri, 14 Jul 2023 16:02:53 -0500 Subject: [PATCH 01/26] initial commit adding xrpl.js and switching to typescript. --- .gitignore | 14 +- index.js | 45 +- package-lock.json | 3065 +++++++++++++++++++++++++++++++++++-- package.json | 13 +- src/client.ts | 17 + src/destination-wallet.ts | 30 + src/index.ts | 124 ++ src/types.ts | 6 + src/wallet.ts | 10 + tsconfig.json | 20 + 10 files changed, 3186 insertions(+), 158 deletions(-) create mode 100644 src/client.ts create mode 100644 src/destination-wallet.ts create mode 100644 src/index.ts create mode 100644 src/types.ts create mode 100644 src/wallet.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index c221276..4b77eee 100644 --- a/.gitignore +++ b/.gitignore @@ -23,22 +23,16 @@ coverage # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt -# Bower dependency directory (https://bower.io/) -bower_components - # node-waf configuration .lock-wscript -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release +# Ignore dist folder, built from tsc +dist # Dependency directories node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ - # Optional npm cache directory .npm @@ -71,3 +65,7 @@ typings/ # Serverless directories .serverless + +# Ignore IntelliJ files +.idea +*.iml diff --git a/index.js b/index.js index 9961b06..8949f6b 100644 --- a/index.js +++ b/index.js @@ -3,10 +3,10 @@ const express = require('express') const cors = require('cors') const app = express() const port = process.env['PORT'] -const RippleAPI = require('ripple-lib').RippleAPI const addressCodec = require('ripple-address-codec') const { BigQuery } = require("@google-cloud/bigquery"); - +const {classicAddressToXAddress, isValidClassicAddress, xAddressToClassicAddress} = require('ripple-address-codec') +const xrpl = require('xrpl') const rippledUri = process.env['RIPPLED_URI'] const address = process.env['FUNDING_ADDRESS'] @@ -51,7 +51,7 @@ function createRippleAPI() { return } - api = new RippleAPI({ + api = new xrpl.Client({ server: rippledUri }) @@ -60,16 +60,6 @@ function createRippleAPI() { console.log(error) }) - if (api.connection._ws) { - console.log('setting _ws error handler') - api.connection._ws.on('error', error => { - console.log('_ws error: ' + error) - console.log(error) - }) - } else { - console.log('no _ws yet') - } - api.on('error', (errorCode, errorMessage) => { console.log('RippleAPI error: ' + errorCode + ': ' + errorMessage) }) @@ -106,18 +96,18 @@ app.post('/accounts', (req, res) => { createRippleAPI() let account if (req.body.destination) { - if (api.isValidAddress(req.body.destination)) { + if (isValidClassicAddress(req.body.destination)) { let xAddress let classicAddress let tag if (req.body.destination.startsWith('T')) { - const t = addressCodec.xAddressToClassicAddress(req.body.destination) + const t = xAddressToClassicAddress(req.body.destination) xAddress = req.body.destination classicAddress = t.classicAddress tag = t.tag } else { - xAddress = addressCodec.classicAddressToXAddress(req.body.destination, false, true) + xAddress = classicAddressToXAddress(req.body.destination, false, true) classicAddress = req.body.destination } account = { @@ -169,20 +159,9 @@ app.post('/accounts', (req, res) => { }).then(sequence => { console.log(`${reqId}| Preparing payment with destination=${account.address}, sequence: ${sequence}`) const payment = { - source: { - address: address, - maxAmount: { - value: amount, - currency: 'XRP' - } - }, - destination: { - address: account.address, - amount: { - value: amount, - currency: 'XRP' - } - }, + Account: address, + Amount: amount, + destination: account.address, memos: req.body.memos ? [...req.body.memos] : [], } if (account.tag) payment.destination.tag = account.tag @@ -213,7 +192,7 @@ app.post('/accounts', (req, res) => { memos: memos, account: account.xAddress, amount: amount, - sequence: sequence, + sequence: sequence, }, ]; const bigquery = new BigQuery( @@ -238,7 +217,7 @@ app.post('/accounts', (req, res) => { }); console.log("inserted big query") } - + /// prepare res if (!req.body.destination) { response.balance = Number(amount) @@ -459,4 +438,4 @@ setInterval(() => { console.log(`[TPS] success=${txCount}, tps=${(txCount / 60).toFixed(1)}, peak=${peak}, requests=${txRequestCount}, rps=${(txRequestCount / 60).toFixed(1)}, peakRequests=${peakRequests}, success%=${((txCount / txRequestCount) * 100).toFixed(1)}%, success_peak/request_peak=${((peak / peakRequests) * 100).toFixed(1)}%`) txCount = 0 txRequestCount = 0 -}, 60 * 1000) \ No newline at end of file +}, 60 * 1000) diff --git a/package-lock.json b/package-lock.json index 6e02c1d..42cbb47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,24 +1,2175 @@ { "name": "testnet-faucet", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "testnet-faucet", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@google-cloud/bigquery": "^6.2.0", + "cors": "^2.8.4", + "express": "^4.16.3", + "ripple-address-codec": "^4.1.0", + "xrpl": "^2.9.0" + }, + "devDependencies": { + "@types/cors": "^2.8.13", + "@types/express": "^4.17.17", + "typescript": "^5.1.6" + } + }, + "node_modules/@google-cloud/bigquery": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@google-cloud/bigquery/-/bigquery-6.2.1.tgz", + "integrity": "sha512-C/tcM3jQ3RU8pKHHxj702ouIfGZ9GAQ5U+ZpvS/o4B3yWtqmnG3TITL5oRnzDjEKeMTNu5C6z3/nFtix3GKlqA==", + "dependencies": { + "@google-cloud/common": "^4.0.0", + "@google-cloud/paginator": "^4.0.0", + "@google-cloud/precise-date": "^3.0.1", + "@google-cloud/promisify": "^3.0.0", + "arrify": "^2.0.1", + "big.js": "^6.0.0", + "duplexify": "^4.0.0", + "extend": "^3.0.2", + "is": "^3.3.0", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/common": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-4.0.3.tgz", + "integrity": "sha512-fUoMo5b8iAKbrYpneIRV3z95AlxVJPrjpevxs4SKoclngWZvTXBSGpNisF5+x5m+oNGve7jfB1e6vNBZBUs7Fw==", + "dependencies": { + "@google-cloud/projectify": "^3.0.0", + "@google-cloud/promisify": "^3.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^8.0.2", + "retry-request": "^5.0.0", + "teeny-request": "^8.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/paginator": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-4.0.1.tgz", + "integrity": "sha512-6G1ui6bWhNyHjmbYwavdN7mpVPRBtyDg/bfqBTAlwr413On2TnFNfDxc9UhTJctkgoCDgQXEKiRPLPR9USlkbQ==", + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/precise-date": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-3.0.1.tgz", + "integrity": "sha512-crK2rgNFfvLoSgcKJY7ZBOLW91IimVNmPfi1CL+kMTf78pTJYd29XqEVedAeBu4DwCJc0EDIp1MpctLgoPq+Uw==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/projectify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz", + "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz", + "integrity": "sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.4.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", + "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", + "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dependencies": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "engines": { + "node": ">=8" + } + }, + "node_modules/assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "dependencies": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/big.js": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.1.tgz", + "integrity": "sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==", + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, + "node_modules/bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", + "engines": { + "node": "*" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bip32": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", + "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", + "dependencies": { + "@types/node": "10.12.18", + "bs58check": "^2.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "tiny-secp256k1": "^1.1.3", + "typeforce": "^1.11.5", + "wif": "^2.0.6" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bip32/node_modules/@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + }, + "node_modules/bip39": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz", + "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==", + "dependencies": { + "@noble/hashes": "^1.2.0" + } + }, + "node_modules/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA==" + }, + "node_modules/body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "dependencies": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.1", + "http-errors": "~1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "~2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "~1.6.15" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "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/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/bs58check/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/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/cors": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", + "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decimal.js": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", + "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==" + }, + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "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/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/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/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==" + }, + "node_modules/es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.16.3", + "resolved": "http://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "dependencies": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.3", + "qs": "6.5.1", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/fast-text-encoding": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", + "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "node_modules/finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gaxios/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/gaxios/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gaxios/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==" + }, + "node_modules/gcp-metadata": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", + "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", + "dependencies": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/google-auth-library": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz", + "integrity": "sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==", + "dependencies": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^5.0.0", + "gcp-metadata": "^5.3.0", + "gtoken": "^6.1.0", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/google-p12-pem": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", + "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", + "dependencies": { + "node-forge": "^1.3.1" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gtoken": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", + "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "dependencies": { + "gaxios": "^5.0.1", + "google-p12-pem": "^4.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "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/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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==" + }, + "node_modules/iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "engines": { + "node": "*" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/md5.js/node_modules/safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "bin": { + "mime": "cli.js" + } + }, + "node_modules/mime-db": { + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", + "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", + "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", + "dependencies": { + "mime-db": "~1.36.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" + }, + "node_modules/negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "dependencies": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dependencies": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": ">= 1.3.1 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body/node_modules/setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/retry-request": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz", + "integrity": "sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==", + "dependencies": { + "debug": "^4.1.1", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/retry-request/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/retry-request/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==" + }, + "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/ripple-address-codec": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-4.3.0.tgz", + "integrity": "sha512-Tvd81i7hpDmNqHvkj6iYlj8Tv3I1Romw5gfjni9eacewJvGV2xe+p2y0FAw39z72qfciRMhQyHvpnviBcWVBNw==", + "dependencies": { + "base-x": "^3.0.9", + "create-hash": "^1.1.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/ripple-keypairs": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-1.3.0.tgz", + "integrity": "sha512-LzM3Up9Pwz3dYqnczzNptimN3AxtjeGbDGeiOzREzbkslKiZcJ615b/ghBN4H23SC6W1GAL95juEzzimDi4THw==", + "dependencies": { + "bn.js": "^5.1.1", + "brorand": "^1.0.5", + "elliptic": "^6.5.4", + "hash.js": "^1.0.3", + "ripple-address-codec": "^4.3.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "node_modules/send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "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/string_decoder/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/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==" + }, + "node_modules/teeny-request": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.3.tgz", + "integrity": "sha512-jJZpA5He2y52yUhA7pyAGZlgQpcB+xLjcN0eUFxr9c8hP/H7uOXbBNVo/O0C/xVfJLJs680jvkFgVJEEvk9+ww==", + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/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==" + }, + "node_modules/tiny-secp256k1": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", + "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.3.0", + "bn.js": "^4.11.8", + "create-hmac": "^1.1.7", + "elliptic": "^6.4.0", + "nan": "^2.13.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tiny-secp256k1/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/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" + }, + "node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "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/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.10.tgz", + "integrity": "sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wif": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", + "integrity": "sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==", + "dependencies": { + "bs58check": "<3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/xrpl": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-2.9.0.tgz", + "integrity": "sha512-bzXWIom5PAVOldObPnTU/6Zbcv234+aeBuMKTipxJptXiQRC1slm3ifluQT1EAedAmu/COngRAGcQskWINxmjg==", + "dependencies": { + "bignumber.js": "^9.0.0", + "bip32": "^2.0.6", + "bip39": "^3.0.4", + "https-proxy-agent": "^5.0.0", + "lodash": "^4.17.4", + "ripple-address-codec": "^4.3.0", + "ripple-binary-codec": "^1.7.0", + "ripple-keypairs": "^1.3.0", + "ws": "^8.2.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/xrpl/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/xrpl/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/xrpl/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/xrpl/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==" + }, + "node_modules/xrpl/node_modules/ripple-binary-codec": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-1.7.0.tgz", + "integrity": "sha512-UYDEOHaJ44rvGl9TxGM2jB0PxdhPaKLmozdMrOlrLgK6bEFWxBgdJ61IWHIGRa1bEPa2TdqsQVB2gyVEA/t54w==", + "dependencies": { + "assert": "^2.0.0", + "big-integer": "^1.6.48", + "buffer": "6.0.3", + "create-hash": "^1.2.0", + "decimal.js": "^10.2.0", + "ripple-address-codec": "^4.3.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/xrpl/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + }, "dependencies": { - "@types/lodash": { - "version": "4.14.149", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", - "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==" + "@google-cloud/bigquery": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@google-cloud/bigquery/-/bigquery-6.2.1.tgz", + "integrity": "sha512-C/tcM3jQ3RU8pKHHxj702ouIfGZ9GAQ5U+ZpvS/o4B3yWtqmnG3TITL5oRnzDjEKeMTNu5C6z3/nFtix3GKlqA==", + "requires": { + "@google-cloud/common": "^4.0.0", + "@google-cloud/paginator": "^4.0.0", + "@google-cloud/precise-date": "^3.0.1", + "@google-cloud/promisify": "^3.0.0", + "arrify": "^2.0.1", + "big.js": "^6.0.0", + "duplexify": "^4.0.0", + "extend": "^3.0.2", + "is": "^3.3.0", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + } + }, + "@google-cloud/common": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-4.0.3.tgz", + "integrity": "sha512-fUoMo5b8iAKbrYpneIRV3z95AlxVJPrjpevxs4SKoclngWZvTXBSGpNisF5+x5m+oNGve7jfB1e6vNBZBUs7Fw==", + "requires": { + "@google-cloud/projectify": "^3.0.0", + "@google-cloud/promisify": "^3.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^8.0.2", + "retry-request": "^5.0.0", + "teeny-request": "^8.0.0" + } + }, + "@google-cloud/paginator": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-4.0.1.tgz", + "integrity": "sha512-6G1ui6bWhNyHjmbYwavdN7mpVPRBtyDg/bfqBTAlwr413On2TnFNfDxc9UhTJctkgoCDgQXEKiRPLPR9USlkbQ==", + "requires": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + } + }, + "@google-cloud/precise-date": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-3.0.1.tgz", + "integrity": "sha512-crK2rgNFfvLoSgcKJY7ZBOLW91IimVNmPfi1CL+kMTf78pTJYd29XqEVedAeBu4DwCJc0EDIp1MpctLgoPq+Uw==" + }, + "@google-cloud/projectify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz", + "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==" + }, + "@google-cloud/promisify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz", + "integrity": "sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA==" + }, + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==" + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "@types/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==", + "dev": true + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true }, "@types/node": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.0.tgz", - "integrity": "sha512-0ARSQootUG1RljH2HncpsY2TJBfGQIKOOi7kxzUY6z54ePu/ZD+wJA8zI2Q6v8rol2qpG/rvqsReco8zNMPvhQ==" + "version": "20.4.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", + "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==", + "dev": true + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } }, - "@types/ws": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.2.2.tgz", - "integrity": "sha512-oqnI3DbGCVI9zJ/WHdFo3CUE8jQ8CVQDUIKaDtlTcNeT4zs6UCg9Gvk5QrFx2QPkRszpM6yc8o0p4aGjCsTi+w==", + "@types/serve-static": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", + "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", + "dev": true, "requires": { + "@types/http-errors": "*", + "@types/mime": "*", "@types/node": "*" } }, @@ -31,30 +2182,97 @@ "negotiator": "0.6.1" } }, - "agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==" - }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + }, + "assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "requires": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "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": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "safe-buffer": "^5.0.1" } }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" + }, + "big.js": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.1.tgz", + "integrity": "sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==" + }, "bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bip32": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", + "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", + "requires": { + "@types/node": "10.12.18", + "bs58check": "^2.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "tiny-secp256k1": "^1.1.3", + "typeforce": "^1.11.5", + "wif": "^2.0.6" + }, + "dependencies": { + "@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + } + } + }, + "bip39": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz", + "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==", + "requires": { + "@noble/hashes": "^1.2.0" + } + }, "bn.js": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.1.tgz", @@ -82,11 +2300,59 @@ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, + "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" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "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==" + } + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -116,11 +2382,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" - }, "cors": { "version": "2.8.4", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", @@ -142,6 +2403,19 @@ "sha.js": "^2.4.0" } }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -155,6 +2429,15 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==" }, + "define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -165,6 +2448,25 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -201,6 +2503,24 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==" + }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -248,6 +2568,21 @@ "vary": "~1.1.2" } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "fast-text-encoding": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", + "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "finalhandler": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -262,6 +2597,14 @@ "unpipe": "~1.0.0" } }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -272,6 +2615,150 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "requires": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "gcp-metadata": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", + "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", + "requires": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + } + }, + "get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + } + }, + "google-auth-library": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz", + "integrity": "sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^5.0.0", + "gcp-metadata": "^5.3.0", + "gtoken": "^6.1.0", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + } + }, + "google-p12-pem": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", + "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", + "requires": { + "node-forge": "^1.3.1" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "gtoken": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", + "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "requires": { + "gaxios": "^5.0.1", + "google-p12-pem": "^4.0.0", + "jws": "^4.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, "hash-base": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", @@ -311,21 +2798,30 @@ "statuses": ">= 1.4.0 < 2" } }, - "https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "requires": { - "agent-base": "5", + "@tootallnate/once": "2", + "agent-base": "6", "debug": "4" }, "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -340,6 +2836,11 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -350,20 +2851,98 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" }, - "jsonschema": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.2.tgz", - "integrity": "sha512-iX5OFQ6yx9NgbHCwse51ohhKgLuLL7Z5cNOeZOPIlDUtAMrxlruHLzVZxbltdHE5mEDXN+75oFOwq6Gn0MZwsA==" + "is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==" + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } }, "md5.js": { "version": "1.3.5", @@ -430,16 +3009,48 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, + "node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -448,6 +3059,14 @@ "ee-first": "1.1.1" } }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", @@ -511,10 +3130,39 @@ } } }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "retry-request": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz", + "integrity": "sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==", + "requires": { + "debug": "^4.1.1", + "extend": "^3.0.2" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } }, "ripemd160": { "version": "2.0.2", @@ -526,76 +3174,24 @@ } }, "ripple-address-codec": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-4.1.0.tgz", - "integrity": "sha512-C72gJpwvDagaOUiHyh67otqNqFduB4hjvJFiiPz/8I3FCiUYuvFLXeLhb29CEkoAEdoN9p7pPreLgoHUvwzt9w==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-4.3.0.tgz", + "integrity": "sha512-Tvd81i7hpDmNqHvkj6iYlj8Tv3I1Romw5gfjni9eacewJvGV2xe+p2y0FAw39z72qfciRMhQyHvpnviBcWVBNw==", "requires": { - "base-x": "3.0.7", + "base-x": "^3.0.9", "create-hash": "^1.1.2" - }, - "dependencies": { - "base-x": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.7.tgz", - "integrity": "sha512-zAKJGuQPihXW22fkrfOclUUZXM2g92z5GzlSMHxhO6r6Qj+Nm0ccaGNBzDZojzwOMkpjAv4J0fOv1U4go+a4iw==", - "requires": { - "safe-buffer": "^5.0.1" - } - } - } - }, - "ripple-binary-codec": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.2.6.tgz", - "integrity": "sha512-k0efyjpcde7p+rJ9PXW9tJSYsUDdlC9Z9xU7OPM7fJiHVKlR1E7nfu0jqw9vVXtTG3tujqKeEgtcb8yaa7rMXA==", - "requires": { - "babel-runtime": "^6.6.1", - "bn.js": "^5.1.1", - "create-hash": "^1.1.2", - "decimal.js": "^10.2.0", - "inherits": "^2.0.1", - "lodash": "^4.17.15", - "ripple-address-codec": "^4.0.0" } }, "ripple-keypairs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-1.0.0.tgz", - "integrity": "sha512-MQ3d6fU3D+Cqu5ma4dfkfa+KakN2sKpVVVN0FeJyAYPVIGXu8Rcvd1g028TdwYAZcSYk0tGn5UhHxd0gUG3T8g==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-1.3.0.tgz", + "integrity": "sha512-LzM3Up9Pwz3dYqnczzNptimN3AxtjeGbDGeiOzREzbkslKiZcJ615b/ghBN4H23SC6W1GAL95juEzzimDi4THw==", "requires": { "bn.js": "^5.1.1", "brorand": "^1.0.5", - "elliptic": "^6.5.2", + "elliptic": "^6.5.4", "hash.js": "^1.0.3", - "ripple-address-codec": "^4.0.0" - } - }, - "ripple-lib": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/ripple-lib/-/ripple-lib-1.6.4.tgz", - "integrity": "sha512-6PDooo5b23oY89WVQ+IqTDerG8FXYeu4ZuprrIbqe7/A3Iy6zCGatez9AcNMgUnq+bCk3P/oylJXrBYLqpe3bA==", - "requires": { - "@types/lodash": "^4.14.136", - "@types/ws": "^7.2.0", - "bignumber.js": "^9.0.0", - "https-proxy-agent": "^4.0.0", - "jsonschema": "1.2.2", - "lodash": "^4.17.4", - "lodash.isequal": "^4.5.0", - "ripple-address-codec": "^4.0.0", - "ripple-binary-codec": "^0.2.5", - "ripple-keypairs": "^1.0.0", - "ripple-lib-transactionparser": "0.8.2", - "ws": "^7.2.0" - } - }, - "ripple-lib-transactionparser": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.8.2.tgz", - "integrity": "sha512-1teosQLjYHLyOQrKUQfYyMjDR3MAq/Ga+MJuLUfpBMypl4LZB4bEoMcmG99/+WVTEiZOezJmH9iCSvm/MyxD+g==", - "requires": { - "bignumber.js": "^9.0.0", - "lodash": "^4.17.15" + "ripple-address-codec": "^4.3.0" } }, "safe-buffer": { @@ -653,6 +3249,107 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" }, + "stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "requires": { + "stubs": "^3.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "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" + }, + "dependencies": { + "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==" + } + } + }, + "stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==" + }, + "teeny-request": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.3.tgz", + "integrity": "sha512-jJZpA5He2y52yUhA7pyAGZlgQpcB+xLjcN0eUFxr9c8hP/H7uOXbBNVo/O0C/xVfJLJs680jvkFgVJEEvk9+ww==", + "requires": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "tiny-secp256k1": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", + "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", + "requires": { + "bindings": "^1.3.0", + "bn.js": "^4.11.8", + "create-hmac": "^1.1.7", + "elliptic": "^6.4.0", + "nan": "^2.13.2" + }, + "dependencies": { + "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==" + } + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -662,25 +3359,165 @@ "mime-types": "~2.1.18" } }, + "typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" + }, + "typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.10.tgz", + "integrity": "sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, + "wif": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", + "integrity": "sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==", + "requires": { + "bs58check": "<3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "xrpl": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-2.9.0.tgz", + "integrity": "sha512-bzXWIom5PAVOldObPnTU/6Zbcv234+aeBuMKTipxJptXiQRC1slm3ifluQT1EAedAmu/COngRAGcQskWINxmjg==", + "requires": { + "bignumber.js": "^9.0.0", + "bip32": "^2.0.6", + "bip39": "^3.0.4", + "https-proxy-agent": "^5.0.0", + "lodash": "^4.17.4", + "ripple-address-codec": "^4.3.0", + "ripple-binary-codec": "^1.7.0", + "ripple-keypairs": "^1.3.0", + "ws": "^8.2.2" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ripple-binary-codec": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-1.7.0.tgz", + "integrity": "sha512-UYDEOHaJ44rvGl9TxGM2jB0PxdhPaKLmozdMrOlrLgK6bEFWxBgdJ61IWHIGRa1bEPa2TdqsQVB2gyVEA/t54w==", + "requires": { + "assert": "^2.0.0", + "big-integer": "^1.6.48", + "buffer": "6.0.3", + "create-hash": "^1.2.0", + "decimal.js": "^10.2.0", + "ripple-address-codec": "^4.3.0" + } + }, + "ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "requires": {} + } + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/package.json b/package.json index ec84aff..c9f845c 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,11 @@ "name": "testnet-faucet", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "dist/index.js", "scripts": { - "start": "node index.js", + "build": "tsc", + "prestart": "npm run build", + "start": "node .", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", @@ -14,6 +16,11 @@ "cors": "^2.8.4", "express": "^4.16.3", "ripple-address-codec": "^4.1.0", - "ripple-lib": "^1.6.4" + "xrpl": "^2.9.0" + }, + "devDependencies": { + "@types/cors": "^2.8.13", + "@types/express": "^4.17.17", + "typescript": "^5.1.6" } } diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 0000000..93c0b0f --- /dev/null +++ b/src/client.ts @@ -0,0 +1,17 @@ +import { Client } from 'xrpl'; + +const rippledUri = process.env['RIPPLED_URI'] + +let instance: Client + +export function connect(): Promise { + if(!!!instance) { + instance = new Client(rippledUri); + } + + if(!instance.isConnected()) { + return instance.connect().then(() => instance) + } + + return Promise.resolve(instance) +} diff --git a/src/destination-wallet.ts b/src/destination-wallet.ts new file mode 100644 index 0000000..3c94d1d --- /dev/null +++ b/src/destination-wallet.ts @@ -0,0 +1,30 @@ +import { classicAddressToXAddress, isValidClassicAddress, xAddressToClassicAddress } from 'ripple-address-codec'; +import { Wallet } from 'xrpl'; +import { Account } from './types'; + +export function getDestinationWallet(address?: string): Account { + if(!isValidClassicAddress(address)) { + throw Error('Invalid destination') + } + + let xAddress + let classicAddress + let tag + + if (address.startsWith('T')) { + const t = xAddressToClassicAddress(address) + xAddress = address + classicAddress = t.classicAddress + tag = t.tag + } else { + xAddress = classicAddressToXAddress(address, false, true) + classicAddress = address + } + + return { + xAddress, + classicAddress, + address: classicAddress, + tag + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..b002892 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,124 @@ +import express from 'express'; +import cors from 'cors' +import { connect } from './client'; +import { Payment, TransactionMetadata, Wallet, xrpToDrops } from 'xrpl'; +import { fundingWallet } from './wallet'; +import { getDestinationWallet } from './destination-wallet'; +import { BigQuery } from '@google-cloud/bigquery'; + +const MAX_AMOUNT = 1000000 +const defaultAmount = process.env['XRP_AMOUNT'] +const port = process.env['PORT'] + +/// bigQuery credentials +const datasetId = process.env['BIGQUERY_DATASET_ID']; +const tableId = process.env['BIGQUERY_TABLE_ID']; +const clientEmail = process.env['BIGQUERY_CLIENT_EMAIL']; +const projectId = process.env['BIGQUERY_PROJECT_ID']; +const privateKey = process.env['BIGQUERY_PRIVATE_KEY'] ? process.env['BIGQUERY_PRIVATE_KEY'].replace(/\\n/g, '\n') : "" + +const app = express() +app.use(cors()) +app.use(express.json()) + +app.post('/accounts', async (req, res) => { + const reqId = (Math.random() + 1).toString(36).substr(2, 5) + const client = await connect() + + let account + + if (req.body.destination) { + try { + account = getDestinationWallet(req.body.destination) + console.log(`${reqId}| User-specified destination: ${account}`) + } catch { + return res.status(400).send({ + error: 'Invalid destination' + }) + } + + } else { + account = Wallet.generate() + console.log(`${reqId}| Generated new account: ${account.address}`) + } + + let amount = defaultAmount + if (req.body.xrpAmount) { + // Disallows fractional XRP + if (!req.body.xrpAmount.match(/^\d+$/)) { + return res.status(400).send({ + error: 'Invalid amount', + detail: 'Must be an integer' + }) + } + let requestedAmountNumber = Number(req.body.xrpAmount) + if (requestedAmountNumber < 0 || requestedAmountNumber > MAX_AMOUNT || typeof requestedAmountNumber !== 'number') { + return res.status(400).send({ + error: 'Invalid amount' + }) + } + amount = requestedAmountNumber.toString() + } + + const payment: Payment = { + TransactionType: 'Payment', + Account: fundingWallet.address, + Amount: xrpToDrops(amount), + Destination: account.address + } + + const { result } = await client.submitAndWait(payment, {wallet: fundingWallet}) + const status = (result.meta as TransactionMetadata).TransactionResult; + const response = { + account, + amount: Number(amount) + } + + if ((status === 'tesSUCCESS' || status === 'terQUEUED')) { + console.log(`${reqId}| Funded ${account.address} with ${amount} XRP (${status})`) + + if (clientEmail && privateKey && projectId) { + const { userAgent = "", usageContext = "" } = req.body; + const memos = req.body.memos ? req.body.memos.map((memo: any) => ({ memo })) : []; + const rows = [ + { + user_agent: userAgent, + usage_context: usageContext, + memos: memos, + account: ('xAddress' in account) ? account.xAddress : account.getXAddress(), + amount: amount, + }, + ]; + const bigquery = new BigQuery( + { + projectId: projectId, + credentials:{ + client_email: clientEmail, + private_key: privateKey, + } + } + ); + + bigquery + .dataset(datasetId) + .table(tableId) + .insert(rows, (error) => { + if (error) { + console.warn("WARNING: Failed to insert into BigQuery", JSON.stringify(error, null, 2)); + } else { + console.log(`Inserted ${rows.length} rows`); + } + }); + console.log("inserted big query") + } + + res.send(response) + } +}) + +app.get('/info', () => { + +}) + +const server = app.listen(port, () => console.log(`Altnet faucet, node version: ${process.version}, listening on port: ${port}`)) +server.setTimeout(20 * 1000) diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..ee1a708 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,6 @@ +export interface Account { + xAddress: string, + classicAddress: string, + address: string, + tag?: number | boolean +} diff --git a/src/wallet.ts b/src/wallet.ts new file mode 100644 index 0000000..ba23379 --- /dev/null +++ b/src/wallet.ts @@ -0,0 +1,10 @@ +import { Wallet } from 'xrpl'; + +const address = process.env['FUNDING_ADDRESS'] +const secret = process.env['FUNDING_SECRET'] + +export const fundingWallet = Wallet.fromSecret(secret) + +if(address !== fundingWallet.address) { + throw Error('Wallet secret and address an incompatible') +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..44e42ef --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true, + "target": "es6", + "noImplicitAny": true, + "moduleResolution": "node", + "sourceMap": true, + "outDir": "dist", + "baseUrl": ".", + "paths": { + "*": [ + "node_modules/*" + ] + } + }, + "include": [ + "src/**/*" + ] +} From c00ac91c9f1ac97c25de88de4bca226f8aa1555e Mon Sep 17 00:00:00 2001 From: Caleb Kniffen Date: Fri, 14 Jul 2023 20:38:49 -0500 Subject: [PATCH 02/26] Use tickets and type configuration --- package-lock.json | 37 ++++++++++++ package.json | 2 + src/client.ts | 27 ++++++--- src/config.ts | 59 +++++++++++++++++++ src/destination-wallet.ts | 1 - src/index.ts | 121 +++----------------------------------- src/routes/accounts.ts | 107 +++++++++++++++++++++++++++++++++ src/routes/index.ts | 14 +++++ src/routes/info.ts | 3 + src/routes/status.ts | 3 + src/ticket-queue.ts | 47 +++++++++++++++ src/wallet.ts | 10 +--- 12 files changed, 299 insertions(+), 132 deletions(-) create mode 100644 src/config.ts create mode 100644 src/routes/accounts.ts create mode 100644 src/routes/index.ts create mode 100644 src/routes/info.ts create mode 100644 src/routes/status.ts create mode 100644 src/ticket-queue.ts diff --git a/package-lock.json b/package-lock.json index 42cbb47..6c3af5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,9 @@ "license": "ISC", "dependencies": { "@google-cloud/bigquery": "^6.2.0", + "cls-rtracer": "^2.6.3", "cors": "^2.8.4", + "dotenv": "^16.3.1", "express": "^4.16.3", "ripple-address-codec": "^4.1.0", "xrpl": "^2.9.0" @@ -483,6 +485,17 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/cls-rtracer": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/cls-rtracer/-/cls-rtracer-2.6.3.tgz", + "integrity": "sha512-O7M/m2M/KfT9v+q7ka9nmsadS67ce9P8+1Zgm6VFamK56oFd1iCoJ9m8hYKUQpK4+RofyaexxHJlOBkxqCDs3Q==", + "dependencies": { + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=12.17.0 <13.0.0 || >=13.14.0 <14.0.0 || >=14.0.0" + } + }, "node_modules/content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -590,6 +603,17 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/duplexify": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", @@ -2362,6 +2386,14 @@ "safe-buffer": "^5.0.1" } }, + "cls-rtracer": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/cls-rtracer/-/cls-rtracer-2.6.3.tgz", + "integrity": "sha512-O7M/m2M/KfT9v+q7ka9nmsadS67ce9P8+1Zgm6VFamK56oFd1iCoJ9m8hYKUQpK4+RofyaexxHJlOBkxqCDs3Q==", + "requires": { + "uuid": "^9.0.0" + } + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -2448,6 +2480,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" + }, "duplexify": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", diff --git a/package.json b/package.json index c9f845c..4cc8e8e 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,9 @@ "license": "ISC", "dependencies": { "@google-cloud/bigquery": "^6.2.0", + "cls-rtracer": "^2.6.3", "cors": "^2.8.4", + "dotenv": "^16.3.1", "express": "^4.16.3", "ripple-address-codec": "^4.1.0", "xrpl": "^2.9.0" diff --git a/src/client.ts b/src/client.ts index 93c0b0f..fe75bf0 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,17 +1,26 @@ import { Client } from 'xrpl'; +import { config } from './config'; -const rippledUri = process.env['RIPPLED_URI'] +const client = new Client(config.RIPPLED_URI); -let instance: Client +client.on('error', (errorCode, errorMessage) => { + console.log('Client error: ' + errorCode + ': ' + errorMessage) +}) -export function connect(): Promise { - if(!!!instance) { - instance = new Client(rippledUri); - } +client.on('connected', () => { + console.log('Client connected') +}) - if(!instance.isConnected()) { - return instance.connect().then(() => instance) +client.on('disconnected', (code) => { + // code - [close code](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) sent by the server + // will be 1000 if this was normal closure + console.log('Client disconnected, code:', code) +}) + +export function connect(): Promise { + if(!client.isConnected()) { + return client.connect().then(() => client) } - return Promise.resolve(instance) + return Promise.resolve(client) } diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..e6bfec1 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,59 @@ +export interface Config { + NODE_ENV: "production" | "development" + PORT: string + RIPPLED_URI: string + FUNDING_ADDRESS: string + FUNDING_SECRET: string + XRP_AMOUNT: string + MAX_AMOUNT: string + MIN_TICKET_COUNT: number + MAX_TICKET_COUNT: number + + BIGQUERY_DATASET_ID?: string + BIGQUERY_TABLE_ID?: string + BIGQUERY_CLIENT_EMAIL?: string + BIGQUERY_PROJECT_ID?: string + BIGQUERY_PRIVATE_KEY?: string +} + +const required: (keyof Config)[] = [ + 'RIPPLED_URI', + 'FUNDING_SECRET' +] + +const defaults: Partial, any>> = { + PORT: '3000', + XRP_AMOUNT: '10000', + MAX_AMOUNT: '1000000', + MIN_TICKET_COUNT: 100, + MAX_TICKET_COUNT: 240, +} + +const dotenv = require('dotenv') + +const result = dotenv.config() + +if (result.error) { + throw result.error +} + +const config = result.parsed; + +// Validate required configuration options +required.forEach((field: string) => { + if(!(field in config)) { + throw new Error(`Config property ${field} is required`) + } +}); + +(new Map(Object.entries(defaults))).forEach((value: string, key: any) => { + console.log(value, key) + // const [key, value] = defaultOption[] + if(!(key in config)) { + config[key] = value + } +}) + +export { + config +} diff --git a/src/destination-wallet.ts b/src/destination-wallet.ts index 3c94d1d..b55b7ce 100644 --- a/src/destination-wallet.ts +++ b/src/destination-wallet.ts @@ -1,5 +1,4 @@ import { classicAddressToXAddress, isValidClassicAddress, xAddressToClassicAddress } from 'ripple-address-codec'; -import { Wallet } from 'xrpl'; import { Account } from './types'; export function getDestinationWallet(address?: string): Account { diff --git a/src/index.ts b/src/index.ts index b002892..914fc60 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,124 +1,17 @@ import express from 'express'; +import rTracer from 'cls-rtracer' import cors from 'cors' -import { connect } from './client'; -import { Payment, TransactionMetadata, Wallet, xrpToDrops } from 'xrpl'; -import { fundingWallet } from './wallet'; -import { getDestinationWallet } from './destination-wallet'; -import { BigQuery } from '@google-cloud/bigquery'; -const MAX_AMOUNT = 1000000 -const defaultAmount = process.env['XRP_AMOUNT'] -const port = process.env['PORT'] - -/// bigQuery credentials -const datasetId = process.env['BIGQUERY_DATASET_ID']; -const tableId = process.env['BIGQUERY_TABLE_ID']; -const clientEmail = process.env['BIGQUERY_CLIENT_EMAIL']; -const projectId = process.env['BIGQUERY_PROJECT_ID']; -const privateKey = process.env['BIGQUERY_PRIVATE_KEY'] ? process.env['BIGQUERY_PRIVATE_KEY'].replace(/\\n/g, '\n') : "" +import accounts from './routes/accounts'; +import { config } from './config'; const app = express() app.use(cors()) app.use(express.json()) +app.use(rTracer.expressMiddleware()) +app.post('/accounts', accounts) -app.post('/accounts', async (req, res) => { - const reqId = (Math.random() + 1).toString(36).substr(2, 5) - const client = await connect() - - let account - - if (req.body.destination) { - try { - account = getDestinationWallet(req.body.destination) - console.log(`${reqId}| User-specified destination: ${account}`) - } catch { - return res.status(400).send({ - error: 'Invalid destination' - }) - } - - } else { - account = Wallet.generate() - console.log(`${reqId}| Generated new account: ${account.address}`) - } - - let amount = defaultAmount - if (req.body.xrpAmount) { - // Disallows fractional XRP - if (!req.body.xrpAmount.match(/^\d+$/)) { - return res.status(400).send({ - error: 'Invalid amount', - detail: 'Must be an integer' - }) - } - let requestedAmountNumber = Number(req.body.xrpAmount) - if (requestedAmountNumber < 0 || requestedAmountNumber > MAX_AMOUNT || typeof requestedAmountNumber !== 'number') { - return res.status(400).send({ - error: 'Invalid amount' - }) - } - amount = requestedAmountNumber.toString() - } - - const payment: Payment = { - TransactionType: 'Payment', - Account: fundingWallet.address, - Amount: xrpToDrops(amount), - Destination: account.address - } - - const { result } = await client.submitAndWait(payment, {wallet: fundingWallet}) - const status = (result.meta as TransactionMetadata).TransactionResult; - const response = { - account, - amount: Number(amount) - } - - if ((status === 'tesSUCCESS' || status === 'terQUEUED')) { - console.log(`${reqId}| Funded ${account.address} with ${amount} XRP (${status})`) - - if (clientEmail && privateKey && projectId) { - const { userAgent = "", usageContext = "" } = req.body; - const memos = req.body.memos ? req.body.memos.map((memo: any) => ({ memo })) : []; - const rows = [ - { - user_agent: userAgent, - usage_context: usageContext, - memos: memos, - account: ('xAddress' in account) ? account.xAddress : account.getXAddress(), - amount: amount, - }, - ]; - const bigquery = new BigQuery( - { - projectId: projectId, - credentials:{ - client_email: clientEmail, - private_key: privateKey, - } - } - ); - - bigquery - .dataset(datasetId) - .table(tableId) - .insert(rows, (error) => { - if (error) { - console.warn("WARNING: Failed to insert into BigQuery", JSON.stringify(error, null, 2)); - } else { - console.log(`Inserted ${rows.length} rows`); - } - }); - console.log("inserted big query") - } - - res.send(response) - } +const server = app.listen(config.PORT, () => { + console.log(`Faucet, node version: ${process.version}, listening on port: ${config.PORT}`) }) - -app.get('/info', () => { - -}) - -const server = app.listen(port, () => console.log(`Altnet faucet, node version: ${process.version}, listening on port: ${port}`)) server.setTimeout(20 * 1000) diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts new file mode 100644 index 0000000..c90c54c --- /dev/null +++ b/src/routes/accounts.ts @@ -0,0 +1,107 @@ +import { Request, Response } from 'express' +import { connect } from '../client'; +import { getDestinationWallet } from '../destination-wallet'; +import { Payment, Wallet, xrpToDrops } from 'xrpl'; +import { fundingWallet } from '../wallet'; +import { BigQuery } from '@google-cloud/bigquery'; +import { config } from '../config'; +import { getTicket } from '../ticket-queue'; +import rTracer from 'cls-rtracer'; + +export default async function(req: Request, res: Response) { + const reqId = (Math.random() + 1).toString(36).substr(2, 5) + const client = await connect() + + let account + + if (req.body.destination) { + try { + account = getDestinationWallet(req.body.destination) + console.log(`${rTracer.id()} | User-specified destination: ${account}`) + } catch { + return res.status(400).send({ + error: 'Invalid destination' + }) + } + + } else { + account = Wallet.generate() + console.log(`${rTracer.id()} | Generated new account: ${account.address}`) + } + + let amount = config.XRP_AMOUNT + if (req.body.xrpAmount) { + // Disallows fractional XRP + if (!req.body.xrpAmount.match(/^\d+$/)) { + return res.status(400).send({ + error: 'Invalid amount', + detail: 'Must be an integer' + }) + } + let requestedAmountNumber = Number(req.body.xrpAmount) + if (requestedAmountNumber < 0 || requestedAmountNumber > config.MAX_AMOUNT || typeof requestedAmountNumber !== 'number') { + return res.status(400).send({ + error: 'Invalid amount' + }) + } + amount = requestedAmountNumber.toString() + } + + const payment: Payment = { + TransactionType: 'Payment', + Account: fundingWallet.address, + Amount: xrpToDrops(amount), + Destination: account.address, + Sequence: 0, + TicketSequence: await getTicket(client) + } + + const { result } = await client.submit(payment, {wallet: fundingWallet}) + const status = result.engine_result + const response = { + account, + amount: Number(amount) + } + + // TODO: check for tefNO_TICKET and try again with another ticket + if ((status === 'tesSUCCESS' || status === 'terQUEUED')) { + console.log(`${reqId} | Funded ${account.address} with ${amount} XRP (${status})`) + + if (config.BIGQUERY_PROJECT_ID) { + const { userAgent = "", usageContext = "" } = req.body; + const memos = req.body.memos ? req.body.memos.map((memo: any) => ({ memo })) : []; + const rows = [ + { + user_agent: userAgent, + usage_context: usageContext, + memos: memos, + account: ('xAddress' in account) ? account.xAddress : account.getXAddress(), + amount: amount, + }, + ]; + const bigquery = new BigQuery( + { + projectId: config.BIGQUERY_PROJECT_ID, + credentials:{ + client_email: config.BIGQUERY_CLIENT_EMAIL, + private_key: config.BIGQUERY_PRIVATE_KEY, + } + } + ); + + bigquery + .dataset(config.BIGQUERY_DATASET_ID) + .table(config.BIGQUERY_TABLE_ID) + .insert(rows, (error) => { + if (error) { + console.warn("WARNING: Failed to insert into BigQuery", JSON.stringify(error, null, 2)); + } else { + console.log(`Inserted ${rows.length} rows`); + } + }); + console.log("inserted big query") + } + + res.send(response) + } +} diff --git a/src/routes/index.ts b/src/routes/index.ts new file mode 100644 index 0000000..4aae306 --- /dev/null +++ b/src/routes/index.ts @@ -0,0 +1,14 @@ +import { Router } from 'express' +import accounts from './accounts'; +import info from './info'; +import status from './status'; + +const router = Router() + +router.post('accounts', accounts) +router.post('info', info) +router.post('status', status) + +export { + router +} diff --git a/src/routes/info.ts b/src/routes/info.ts new file mode 100644 index 0000000..7b05ed5 --- /dev/null +++ b/src/routes/info.ts @@ -0,0 +1,3 @@ +import { Request, Response } from 'express'; + +export default async function(req: Request, res: Response) {} diff --git a/src/routes/status.ts b/src/routes/status.ts new file mode 100644 index 0000000..7b05ed5 --- /dev/null +++ b/src/routes/status.ts @@ -0,0 +1,3 @@ +import { Request, Response } from 'express'; + +export default async function(req: Request, res: Response) {} diff --git a/src/ticket-queue.ts b/src/ticket-queue.ts new file mode 100644 index 0000000..dde6a60 --- /dev/null +++ b/src/ticket-queue.ts @@ -0,0 +1,47 @@ +import { Client, isCreatedNode, TicketCreate, TransactionMetadata, TxResponse } from 'xrpl'; +import { fundingWallet } from './wallet'; +import { Ticket } from 'xrpl/dist/npm/models/ledger'; +import { config } from './config'; +import rTracer from 'cls-rtracer'; + +const ticketQueue: number[] = [] +let createTicketsPromise: Promise>; + +// TODO: Populate the ticket queue on disconnect or every once in a while to account for tickets used by other systems +export async function getTicket(client: Client) { + if(ticketQueue.length < config.MIN_TICKET_COUNT && !createTicketsPromise) { + const ticketsToCreate = Math.min(140, config.MAX_TICKET_COUNT - ticketQueue.length); + console.log(`Creating ${ticketsToCreate} tickets. ${ticketQueue.length} tickets remaining.`) + createTicketsPromise = client.submitAndWait( + { + TransactionType: 'TicketCreate', + TicketCount: Math.min(140, config.MAX_TICKET_COUNT - ticketQueue.length), + Account: fundingWallet.address + }, + { + wallet: fundingWallet + } + ).then((response) => { + let ticketsCreated = 0; + // Populate the ticket queue with newly created tickets + (response.result.meta as TransactionMetadata).AffectedNodes.forEach((node: any) => { + if(isCreatedNode(node) && node.CreatedNode.LedgerEntryType === 'Ticket'){ + ticketsCreated++; + ticketQueue.push((node.CreatedNode.NewFields as Partial).TicketSequence) + } + }) + console.log(`Created tickets. Tx: ${response.result.hash}`); + createTicketsPromise = null + }).catch((response) => { + console.log(`Failed to create tickets. Tx: ${response.result.hash}`); + }) + } + + if(ticketQueue.length === 0){ + console.log(`${rTracer.id()} | Waiting for tickets to be created.`) + await createTicketsPromise + return ticketQueue.shift() + } + + return Promise.resolve(ticketQueue.shift()) +} diff --git a/src/wallet.ts b/src/wallet.ts index ba23379..3909bcd 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -1,10 +1,4 @@ import { Wallet } from 'xrpl'; +import { config } from './config'; -const address = process.env['FUNDING_ADDRESS'] -const secret = process.env['FUNDING_SECRET'] - -export const fundingWallet = Wallet.fromSecret(secret) - -if(address !== fundingWallet.address) { - throw Error('Wallet secret and address an incompatible') -} +export const fundingWallet = Wallet.fromSecret(config.FUNDING_SECRET) From 7707f8f28c10406b2175b718fca3e37e5e4331bc Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Mon, 17 Jul 2023 16:27:54 -0700 Subject: [PATCH 03/26] add info route --- src/routes/info.ts | 61 ++++++++++++++++++++++++++++++++++++++++++++-- src/utils.ts | 16 ++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 src/utils.ts diff --git a/src/routes/info.ts b/src/routes/info.ts index 7b05ed5..b340096 100644 --- a/src/routes/info.ts +++ b/src/routes/info.ts @@ -1,3 +1,60 @@ -import { Request, Response } from 'express'; +const os = require("os"); +import { Request, Response } from "express"; +import { connect } from "../client"; +import { fundingWallet } from "../wallet"; +import { ServerInfoResponse } from "ripple-lib/dist/npm/common/types/commands"; +import { checkForWarning, format } from "../utils"; +import { AccountInfoResponse, FeeResponse } from "xrpl"; -export default async function(req: Request, res: Response) {} +export default async function (req: Request, res: Response) { + let client = await connect(); + try { + const serverInfo: ServerInfoResponse = await client.request({ + command: "server_info", + }); + checkForWarning(serverInfo); + const feeInfo: FeeResponse = await client.request({ + command: "fee", + }); + checkForWarning(feeInfo); + const accountInfo: AccountInfoResponse = await client.request({ + command: "account_info", + params: { + account: fundingWallet.classicAddress, + ledger_index: "current", + queue: true, + }, + }); + console.log( + "Returning /info - ledgerVersion: " + + serverInfo.validatedLedger.ledgerVersion + + ", age: " + + serverInfo.validatedLedger.age + + ", expected_ledger_size: " + + feeInfo.result.expected_ledger_size + + ", open_ledger_fee: " + + feeInfo.result.drops.open_ledger_fee + + ", hostID: " + + serverInfo.hostID + ); + const processUptime = process.uptime(); + const osUptime = os.uptime(); + res.send({ + faucetVersion: "0.0.2", + processUptime, + processUptimeHhMmSs: format(processUptime), + osUptime, + osUptimeHhMmSs: format(osUptime), + balance: accountInfo.result.account_data.Balance, + rippled: accountInfo, + feeInfo, + }); + } catch (error) { + console.error("/info error:", error); + res.status(500).send({ + error: "Server load is too high. Request info later", + }); + await client.disconnect(); + client = await connect(); + } +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..991f187 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,16 @@ +import { connect } from "./client"; +export function format(seconds: number) { + function pad(s: number) { + return (s < 10 ? "0" : "") + s; + } + let hours = Math.floor(seconds / (60 * 60)); + let minutes = Math.floor((seconds % (60 * 60)) / 60); + seconds = Math.floor(seconds % 60); + + return pad(hours) + ":" + pad(minutes) + ":" + pad(seconds); +} +export function checkForWarning(s: any) { + if (s && s.warning) { + console.log("GOT WARNING: " + s.warning); + } +} From 4db4225294cb16e8d1a5cd875050d17845a70552 Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 18 Jul 2023 15:23:58 -0700 Subject: [PATCH 04/26] revamp client, accounts with ticket retry logic --- src/client.ts | 72 +++++++++++---- src/index.ts | 65 +++++++++++--- src/routes/accounts.ts | 195 ++++++++++++++++++++++++++--------------- src/routes/info.ts | 14 ++- src/utils.ts | 1 - 5 files changed, 237 insertions(+), 110 deletions(-) diff --git a/src/client.ts b/src/client.ts index fe75bf0..d62ba77 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,26 +1,66 @@ -import { Client } from 'xrpl'; -import { config } from './config'; +import { Client } from "xrpl"; +import { config } from "./config"; -const client = new Client(config.RIPPLED_URI); +let client = new Client(config.RIPPLED_URI); +let clientCreatedDate: number | null = Date.now(); -client.on('error', (errorCode, errorMessage) => { - console.log('Client error: ' + errorCode + ': ' + errorMessage) -}) +client.on("error", (errorCode, errorMessage) => { + console.log("Client error: " + errorCode + ": " + errorMessage); +}); -client.on('connected', () => { - console.log('Client connected') -}) +client.on("connected", () => { + console.log("Client connected"); +}); -client.on('disconnected', (code) => { +client.on("disconnected", (code) => { // code - [close code](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) sent by the server // will be 1000 if this was normal closure - console.log('Client disconnected, code:', code) -}) + console.log("Client disconnected, code:", code); +}); -export function connect(): Promise { - if(!client.isConnected()) { - return client.connect().then(() => client) +export function createClient(): Client { + if (client) { + return client; } + client = new Client(config.RIPPLED_URI); + clientCreatedDate = Date.now(); + return client; +} + +export function getClientCreationDate(): number | null { + return clientCreatedDate; +} + +export async function connect(): Promise { + if (!client.isConnected()) { + await client.connect(); + } + return client; +} + +export async function disconnect(): Promise { + await client.disconnect(); +} +export const MIN_RESET_INTERVAL_MS = 10 * 1000; + +export async function resetClient(reqId: string) { + const clientCreationDate = getClientCreationDate(); + + if (clientCreationDate) { + const clientAge = Date.now() - clientCreationDate; + if (clientAge < MIN_RESET_INTERVAL_MS) { + console.log( + `${reqId}| not resetting client, age=${clientAge} ms < 10 sec` + ); + return; // prevent client from being reset more often than once per 10 sec + } + } + + console.log(`${reqId}| resetting client...`); + await disconnect(); + console.log(`${reqId}| successfully disconnected 'by user'`); - return Promise.resolve(client) + await createClient(); + await connect(); + console.log(`${reqId}| client reconnected.`); } diff --git a/src/index.ts b/src/index.ts index 914fc60..e01d8f8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,56 @@ -import express from 'express'; -import rTracer from 'cls-rtracer' -import cors from 'cors' +import express from "express"; +import rTracer from "cls-rtracer"; +import cors from "cors"; -import accounts from './routes/accounts'; -import { config } from './config'; +import accounts from "./routes/accounts"; +import { config } from "./config"; -const app = express() -app.use(cors()) -app.use(express.json()) -app.use(rTracer.expressMiddleware()) -app.post('/accounts', accounts) +const app = express(); +app.use(cors()); +app.use(express.json()); +app.use(rTracer.expressMiddleware()); +app.post("/accounts", accounts); const server = app.listen(config.PORT, () => { - console.log(`Faucet, node version: ${process.version}, listening on port: ${config.PORT}`) -}) -server.setTimeout(20 * 1000) + console.log( + `Faucet, node version: ${process.version}, listening on port: ${config.PORT}` + ); +}); +server.setTimeout(20 * 1000); + +// In your index.js or wherever txCount is defined +export let txCount = 0; +export let txRequestCount = 0; +export function incrementTxCount() { + txCount += 1; +} +export function incrementTxRequestCount() { + txRequestCount += 1; +} + +// Report TPS every minute +let peak = 0; +let peakRequests = 0; +setInterval(() => { + if (txCount > peak) { + peak = txCount; + } + if (txRequestCount > peakRequests) { + peakRequests = txRequestCount; + } + console.log( + `[TPS] success=${txCount}, tps=${(txCount / 60).toFixed( + 1 + )}, peak=${peak}, requests=${txRequestCount}, rps=${( + txRequestCount / 60 + ).toFixed(1)}, peakRequests=${peakRequests}, success%=${( + (txCount / txRequestCount) * + 100 + ).toFixed(1)}%, success_peak/request_peak=${( + (peak / peakRequests) * + 100 + ).toFixed(1)}%` + ); + txCount = 0; + txRequestCount = 0; +}, 60 * 1000); diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index c90c54c..0469070 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -1,107 +1,158 @@ -import { Request, Response } from 'express' -import { connect } from '../client'; -import { getDestinationWallet } from '../destination-wallet'; -import { Payment, Wallet, xrpToDrops } from 'xrpl'; -import { fundingWallet } from '../wallet'; -import { BigQuery } from '@google-cloud/bigquery'; -import { config } from '../config'; -import { getTicket } from '../ticket-queue'; -import rTracer from 'cls-rtracer'; +import { Request, Response } from "express"; +import { connect, resetClient } from "../client"; +import { getDestinationWallet } from "../destination-wallet"; +import { Client, Payment, Wallet, xrpToDrops } from "xrpl"; +import { fundingWallet } from "../wallet"; +import { BigQuery } from "@google-cloud/bigquery"; +import { config } from "../config"; +import { getTicket } from "../ticket-queue"; +import rTracer from "cls-rtracer"; -export default async function(req: Request, res: Response) { - const reqId = (Math.random() + 1).toString(36).substr(2, 5) - const client = await connect() +export default async function (req: Request, res: Response) { + const reqId = (Math.random() + 1).toString(36).substr(2, 5); + const client = await connect(); - let account + let account; if (req.body.destination) { try { - account = getDestinationWallet(req.body.destination) - console.log(`${rTracer.id()} | User-specified destination: ${account}`) + account = getDestinationWallet(req.body.destination); + console.log(`${rTracer.id()} | User-specified destination: ${account}`); } catch { return res.status(400).send({ - error: 'Invalid destination' - }) + error: "Invalid destination", + }); } - } else { - account = Wallet.generate() - console.log(`${rTracer.id()} | Generated new account: ${account.address}`) + account = Wallet.generate(); + console.log(`${rTracer.id()} | Generated new account: ${account.address}`); } - let amount = config.XRP_AMOUNT + let amount = config.XRP_AMOUNT; if (req.body.xrpAmount) { // Disallows fractional XRP if (!req.body.xrpAmount.match(/^\d+$/)) { return res.status(400).send({ - error: 'Invalid amount', - detail: 'Must be an integer' - }) + error: "Invalid amount", + detail: "Must be an integer", + }); } - let requestedAmountNumber = Number(req.body.xrpAmount) - if (requestedAmountNumber < 0 || requestedAmountNumber > config.MAX_AMOUNT || typeof requestedAmountNumber !== 'number') { + let requestedAmountNumber = Number(req.body.xrpAmount); + if ( + requestedAmountNumber < 0 || + requestedAmountNumber > config.MAX_AMOUNT || + typeof requestedAmountNumber !== "number" + ) { return res.status(400).send({ - error: 'Invalid amount' - }) + error: "Invalid amount", + }); } - amount = requestedAmountNumber.toString() + amount = requestedAmountNumber.toString(); } const payment: Payment = { - TransactionType: 'Payment', + TransactionType: "Payment", Account: fundingWallet.address, Amount: xrpToDrops(amount), Destination: account.address, Sequence: 0, - TicketSequence: await getTicket(client) - } - - const { result } = await client.submit(payment, {wallet: fundingWallet}) - const status = result.engine_result - const response = { - account, - amount: Number(amount) - } + }; + try { + const result = await submitPaymentWithTicket( + payment, + client, + fundingWallet + ); + const status = result.engine_result; + // ... handle the rest of the logic as you have it currently + const response = { + account, + amount: Number(amount), + }; - // TODO: check for tefNO_TICKET and try again with another ticket - if ((status === 'tesSUCCESS' || status === 'terQUEUED')) { - console.log(`${reqId} | Funded ${account.address} with ${amount} XRP (${status})`) + // TODO: check for tefNO_TICKET and try again with another ticket + if (status === "tesSUCCESS" || status === "terQUEUED") { + console.log( + `${reqId} | Funded ${account.address} with ${amount} XRP (${status})` + ); - if (config.BIGQUERY_PROJECT_ID) { - const { userAgent = "", usageContext = "" } = req.body; - const memos = req.body.memos ? req.body.memos.map((memo: any) => ({ memo })) : []; - const rows = [ - { - user_agent: userAgent, - usage_context: usageContext, - memos: memos, - account: ('xAddress' in account) ? account.xAddress : account.getXAddress(), - amount: amount, - }, - ]; - const bigquery = new BigQuery( - { + if (config.BIGQUERY_PROJECT_ID) { + const { userAgent = "", usageContext = "" } = req.body; + const memos = req.body.memos + ? req.body.memos.map((memo: any) => ({ memo })) + : []; + const rows = [ + { + user_agent: userAgent, + usage_context: usageContext, + memos: memos, + account: + "xAddress" in account ? account.xAddress : account.getXAddress(), + amount: amount, + }, + ]; + const bigquery = new BigQuery({ projectId: config.BIGQUERY_PROJECT_ID, - credentials:{ + credentials: { client_email: config.BIGQUERY_CLIENT_EMAIL, private_key: config.BIGQUERY_PRIVATE_KEY, - } - } - ); - - bigquery - .dataset(config.BIGQUERY_DATASET_ID) - .table(config.BIGQUERY_TABLE_ID) - .insert(rows, (error) => { - if (error) { - console.warn("WARNING: Failed to insert into BigQuery", JSON.stringify(error, null, 2)); - } else { - console.log(`Inserted ${rows.length} rows`); - } + }, }); - console.log("inserted big query") + + bigquery + .dataset(config.BIGQUERY_DATASET_ID) + .table(config.BIGQUERY_TABLE_ID) + .insert(rows, (error) => { + if (error) { + console.warn( + "WARNING: Failed to insert into BigQuery", + JSON.stringify(error, null, 2) + ); + } else { + console.log(`Inserted ${rows.length} rows`); + } + }); + console.log("inserted big query"); + } + + res.send(response); + } + } catch (err) { + console.log(`${reqId}| ${err}`); + res.status(500).send({ + error: "Unable to fund account. Server load is too high. Try again later", + account, + }); + await resetClient("accounts"); + } +} + +async function submitPaymentWithTicket( + payment: Payment, + client: Client, + fundingWallet: Wallet, + maxRetries = 3 +) { + let retryCount = 0; + let result; + while (retryCount < maxRetries) { + try { + payment.TicketSequence = await getTicket(client); + result = (await client.submit(payment, { wallet: fundingWallet })).result; + if (result.engine_result === "tefNO_TICKET") { + retryCount++; + console.log(`Retrying transaction (${retryCount}/${maxRetries})`); + } else { + break; + } + } catch (error) { + throw error; // Let the outer try-catch handle other errors } + } - res.send(response) + if (retryCount >= maxRetries) { + throw new Error("Failed to submit transaction after multiple attempts"); } + + return result; } diff --git a/src/routes/info.ts b/src/routes/info.ts index b340096..6d97b7c 100644 --- a/src/routes/info.ts +++ b/src/routes/info.ts @@ -1,10 +1,9 @@ const os = require("os"); import { Request, Response } from "express"; -import { connect } from "../client"; +import { connect, resetClient } from "../client"; import { fundingWallet } from "../wallet"; -import { ServerInfoResponse } from "ripple-lib/dist/npm/common/types/commands"; import { checkForWarning, format } from "../utils"; -import { AccountInfoResponse, FeeResponse } from "xrpl"; +import { AccountInfoResponse, FeeResponse, ServerInfoResponse } from "xrpl"; export default async function (req: Request, res: Response) { let client = await connect(); @@ -27,15 +26,15 @@ export default async function (req: Request, res: Response) { }); console.log( "Returning /info - ledgerVersion: " + - serverInfo.validatedLedger.ledgerVersion + + serverInfo.result.info.build_version + ", age: " + - serverInfo.validatedLedger.age + + serverInfo.result.info.validated_ledger.age + ", expected_ledger_size: " + feeInfo.result.expected_ledger_size + ", open_ledger_fee: " + feeInfo.result.drops.open_ledger_fee + ", hostID: " + - serverInfo.hostID + serverInfo.result.info.hostid ); const processUptime = process.uptime(); const osUptime = os.uptime(); @@ -54,7 +53,6 @@ export default async function (req: Request, res: Response) { res.status(500).send({ error: "Server load is too high. Request info later", }); - await client.disconnect(); - client = await connect(); + await resetClient("info"); } } diff --git a/src/utils.ts b/src/utils.ts index 991f187..aa0d804 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,3 @@ -import { connect } from "./client"; export function format(seconds: number) { function pad(s: number) { return (s < 10 ? "0" : "") + s; From 8e2253535253161c46e38dce32891f34e5563e31 Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 18 Jul 2023 16:21:57 -0700 Subject: [PATCH 05/26] status.ts + small fixes --- src/routes/accounts.ts | 9 ++++---- src/routes/info.ts | 4 ++-- src/routes/status.ts | 48 ++++++++++++++++++++++++++++++++++++++++-- tsconfig.json | 1 + 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index 0469070..05fb2c5 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -9,7 +9,6 @@ import { getTicket } from "../ticket-queue"; import rTracer from "cls-rtracer"; export default async function (req: Request, res: Response) { - const reqId = (Math.random() + 1).toString(36).substr(2, 5); const client = await connect(); let account; @@ -73,7 +72,9 @@ export default async function (req: Request, res: Response) { // TODO: check for tefNO_TICKET and try again with another ticket if (status === "tesSUCCESS" || status === "terQUEUED") { console.log( - `${reqId} | Funded ${account.address} with ${amount} XRP (${status})` + `${rTracer.id()} | Funded ${ + account.address + } with ${amount} XRP (${status})` ); if (config.BIGQUERY_PROJECT_ID) { @@ -118,12 +119,12 @@ export default async function (req: Request, res: Response) { res.send(response); } } catch (err) { - console.log(`${reqId}| ${err}`); + console.log(`${rTracer.id()}| ${err}`); res.status(500).send({ error: "Unable to fund account. Server load is too high. Try again later", account, }); - await resetClient("accounts"); + await resetClient(rTracer.id().toString()); } } diff --git a/src/routes/info.ts b/src/routes/info.ts index 6d97b7c..221fab9 100644 --- a/src/routes/info.ts +++ b/src/routes/info.ts @@ -4,7 +4,7 @@ import { connect, resetClient } from "../client"; import { fundingWallet } from "../wallet"; import { checkForWarning, format } from "../utils"; import { AccountInfoResponse, FeeResponse, ServerInfoResponse } from "xrpl"; - +import * as packageJson from "../../package.json"; export default async function (req: Request, res: Response) { let client = await connect(); try { @@ -39,7 +39,7 @@ export default async function (req: Request, res: Response) { const processUptime = process.uptime(); const osUptime = os.uptime(); res.send({ - faucetVersion: "0.0.2", + faucetVersion: packageJson.version, processUptime, processUptimeHhMmSs: format(processUptime), osUptime, diff --git a/src/routes/status.ts b/src/routes/status.ts index 7b05ed5..a11a18d 100644 --- a/src/routes/status.ts +++ b/src/routes/status.ts @@ -1,3 +1,47 @@ -import { Request, Response } from 'express'; +import { Request, Response } from "express"; +import { connect, resetClient } from "../client"; +import { checkForWarning, format } from "../utils"; +import { FeeResponse, ServerInfoResponse } from "xrpl"; -export default async function(req: Request, res: Response) {} +export default async function (req: Request, res: Response) { + let client = await connect(); + try { + const serverInfo: ServerInfoResponse = await client.request({ + command: "server_info", + }); + checkForWarning(serverInfo.result); + + const feeInfo: FeeResponse = await client.request({ + command: "fee", + }); + checkForWarning(feeInfo.result); + + const processUptime = process.uptime(); + + const text = `*XRP Test Net Faucet:* https://xrpl.org/xrp-testnet-faucet.html +*Uptime:* ${format(processUptime)} +*rippled* buildVersion: ${serverInfo.result.info.build_version} +> completeLedgers: ${serverInfo.result.info.complete_ledgers} +> loadFactor: ${serverInfo.result.info.load_factor} +> peers: ${serverInfo.result.info.peers} +> serverState: ${serverInfo.result.info.server_state} +> validatedLedger.ledgerVersion: ${ + serverInfo.result.info.validated_ledger.seq + } +> hostID: ${serverInfo.result.info.hostid} +> open_ledger_fee: ${feeInfo.result.drops.open_ledger_fee} drops +> expected_ledger_size: ${feeInfo.result.expected_ledger_size} +Full info: https://faucet.altnet.rippletest.net/info`; + + res.send({ + response_type: "in_channel", + text: text, + }); + } catch (error) { + console.log("/status error:", error); + res.status(500).send({ + error: "Server load is too high. Request status later", + }); + await resetClient("status"); + } +} diff --git a/tsconfig.json b/tsconfig.json index 44e42ef..e2014b0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "module": "commonjs", + "resolveJsonModule": true, "esModuleInterop": true, "target": "es6", "noImplicitAny": true, From dc2950d694a441e13b14dc297760fb60483eec27 Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 18 Jul 2023 17:07:19 -0700 Subject: [PATCH 06/26] update routes + logging --- package.json | 2 +- src/index.ts | 4 ++++ src/routes/info.ts | 8 +++----- src/routes/status.ts | 16 +++++++++++++--- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 4cc8e8e..8250626 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "testnet-faucet", "version": "1.0.0", "description": "", - "main": "dist/index.js", + "main": "dist/src/index.js", "scripts": { "build": "tsc", "prestart": "npm run build", diff --git a/src/index.ts b/src/index.ts index e01d8f8..541bb90 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,8 @@ import rTracer from "cls-rtracer"; import cors from "cors"; import accounts from "./routes/accounts"; +import status from "./routes/status"; +import info from "./routes/info"; import { config } from "./config"; const app = express(); @@ -10,6 +12,8 @@ app.use(cors()); app.use(express.json()); app.use(rTracer.expressMiddleware()); app.post("/accounts", accounts); +app.get("/info", info); +app.get("/status", status); const server = app.listen(config.PORT, () => { console.log( diff --git a/src/routes/info.ts b/src/routes/info.ts index 221fab9..c69db2b 100644 --- a/src/routes/info.ts +++ b/src/routes/info.ts @@ -18,11 +18,9 @@ export default async function (req: Request, res: Response) { checkForWarning(feeInfo); const accountInfo: AccountInfoResponse = await client.request({ command: "account_info", - params: { - account: fundingWallet.classicAddress, - ledger_index: "current", - queue: true, - }, + account: fundingWallet.classicAddress, + ledger_index: "current", + queue: true, }); console.log( "Returning /info - ledgerVersion: " + diff --git a/src/routes/status.ts b/src/routes/status.ts index a11a18d..a407112 100644 --- a/src/routes/status.ts +++ b/src/routes/status.ts @@ -15,6 +15,18 @@ export default async function (req: Request, res: Response) { command: "fee", }); checkForWarning(feeInfo.result); + console.log( + "Returning /status - ledgerVersion: " + + serverInfo.result.info.validated_ledger.seq + + ", age: " + + serverInfo.result.info.validated_ledger.age + + ", expected_ledger_size: " + + feeInfo.result.expected_ledger_size + + ", open_ledger_fee: " + + feeInfo.result.drops.open_ledger_fee + + ", hostID: " + + serverInfo.result.info.hostid + ); const processUptime = process.uptime(); @@ -25,9 +37,7 @@ export default async function (req: Request, res: Response) { > loadFactor: ${serverInfo.result.info.load_factor} > peers: ${serverInfo.result.info.peers} > serverState: ${serverInfo.result.info.server_state} -> validatedLedger.ledgerVersion: ${ - serverInfo.result.info.validated_ledger.seq - } +> validatedLedger.ledgerVersion: ${serverInfo.result.info.validated_ledger.seq} > hostID: ${serverInfo.result.info.hostid} > open_ledger_fee: ${feeInfo.result.drops.open_ledger_fee} drops > expected_ledger_size: ${feeInfo.result.expected_ledger_size} From 3136fb282a4c34a760d542d6ba105916dbea518c Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Wed, 19 Jul 2023 11:15:29 -0700 Subject: [PATCH 07/26] delete old file, and small changes --- index.js | 441 ----------------------------------------- src/config.ts | 2 - src/routes/accounts.ts | 1 - 3 files changed, 444 deletions(-) delete mode 100644 index.js diff --git a/index.js b/index.js deleted file mode 100644 index 8949f6b..0000000 --- a/index.js +++ /dev/null @@ -1,441 +0,0 @@ -const os = require('os') -const express = require('express') -const cors = require('cors') -const app = express() -const port = process.env['PORT'] -const addressCodec = require('ripple-address-codec') -const { BigQuery } = require("@google-cloud/bigquery"); -const {classicAddressToXAddress, isValidClassicAddress, xAddressToClassicAddress} = require('ripple-address-codec') -const xrpl = require('xrpl') - -const rippledUri = process.env['RIPPLED_URI'] -const address = process.env['FUNDING_ADDRESS'] -const secret = process.env['FUNDING_SECRET'] -const defaultAmount = process.env['XRP_AMOUNT'] -const MAX_AMOUNT = '1000000' - -/// bigQuery credentials -const datasetId = process.env['BIGQUERY_DATASET_ID']; -const tableId = process.env['BIGQUERY_TABLE_ID']; -const clientEmail = process.env['BIGQUERY_CLIENT_EMAIL']; -const projectId = process.env['BIGQUERY_PROJECT_ID']; -const privateKey = process.env['BIGQUERY_PRIVATE_KEY'] ? process.env['BIGQUERY_PRIVATE_KEY'].replace(/\\n/g, '\n') : "" -app.use(cors()) -app.use(express.json()) - -let txCount = 0 -let txRequestCount = 0 -let api = null -let apiCreatedDate = null - -function resetRippleAPI(reqId) { - if (apiCreatedDate) { - const apiAge = Date.now() - apiCreatedDate - if (apiAge < (10 * 1000)) { - console.log(`${reqId}| not resetting api, age=${apiAge} ms < 10 sec`) - return // prevent api from being reset more often than once per 10 sec - } - } - console.log(`${reqId}| resetting api...`) - let oldApi = api - oldApi.disconnect().then(() => { - console.log(`${reqId}| successfully disconnected 'by user'`) - oldApi = null - }) - api = null - createRippleAPI() -} - -function createRippleAPI() { - if (api) { - return - } - - api = new xrpl.Client({ - server: rippledUri - }) - - api.connection.on('error', error => { - console.log('Connection error: ' + error) - console.log(error) - }) - - api.on('error', (errorCode, errorMessage) => { - console.log('RippleAPI error: ' + errorCode + ': ' + errorMessage) - }) - - api.on('connected', () => { - console.log('RippleAPI connected') - }) - - api.on('disconnected', (code) => { - // code - [close code](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) sent by the server - // will be 1000 if this was normal closure - console.log('RippleAPI disconnected, code:', code) - }) - - apiCreatedDate = Date.now() -} - -function checkForWarning(s) { - if (s && s.warning) { - console.log('GOT WARNING: ' + s.warning) - // TODO: Look for this in the logs - } -} - -let nextAvailableSeq = null - -app.post('/accounts', (req, res) => { - txRequestCount++ - - const reqId = (Math.random() + 1).toString(36).substr(2, 5) - // const reqId = req.ip + req.secure ? ' s' : ' u' - - try { - createRippleAPI() - let account - if (req.body.destination) { - if (isValidClassicAddress(req.body.destination)) { - let xAddress - let classicAddress - let tag - - if (req.body.destination.startsWith('T')) { - const t = xAddressToClassicAddress(req.body.destination) - xAddress = req.body.destination - classicAddress = t.classicAddress - tag = t.tag - } else { - xAddress = classicAddressToXAddress(req.body.destination, false, true) - classicAddress = req.body.destination - } - account = { - xAddress, - classicAddress, - address: classicAddress, - tag - } - } else { - return res.status(400).send({ - error: 'Invalid destination' - }) - } - console.log(`${reqId}| User-specified destination: ${account.xAddress}`) - } else { - account = api.generateAddress({ - test: true - }) - console.log(`${reqId}| Generated new account: ${account.address}`) - } - - let amount = defaultAmount - if (req.body.xrpAmount) { - // Disallows fractional XRP - if (!req.body.xrpAmount.match(/^\d+$/)) { - return res.status(400).send({ - error: 'Invalid amount', - detail: 'Must be an integer' - }) - } - let requestedAmountNumber = Number(req.body.xrpAmount) - if (requestedAmountNumber < 0 || requestedAmountNumber > MAX_AMOUNT || typeof requestedAmountNumber !== 'number') { - return res.status(400).send({ - error: 'Invalid amount' - }) - } - amount = requestedAmountNumber.toString() - } - - api.connect().then(() => { - console.log(`${reqId}| (connected)`) - if (nextAvailableSeq) { - // next tx should use the next seq - nextAvailableSeq++ - return nextAvailableSeq - 1 - } else { - return getSequenceFromAccountInfo({reqId, shouldAdvanceSequence: true}) - } - }).then(sequence => { - console.log(`${reqId}| Preparing payment with destination=${account.address}, sequence: ${sequence}`) - const payment = { - Account: address, - Amount: amount, - destination: account.address, - memos: req.body.memos ? [...req.body.memos] : [], - } - if (account.tag) payment.destination.tag = account.tag - return api.preparePayment(address, payment, {maxLedgerVersionOffset: 5, sequence}) - }).then(prepared => { - checkForWarning(prepared) - - const {signedTransaction} = api.sign(prepared.txJSON, secret) - return api.submit(signedTransaction) - }).then((result, sequence) => { - checkForWarning(result) - - if (result.engine_result === 'tesSUCCESS' || result.engine_result === 'terQUEUED') { - // || result.engine_result === 'terPRE_SEQ' - console.log(`${reqId}| Funded ${account.address} with ${amount} XRP (${result.engine_result})`) - const response = { - account, - amount: Number(amount) - } - - if (clientEmail && privateKey && projectId) { - const { userAgent = "", usageContext = "" } = req.body; - const memos = req.body.memos ? req.body.memos.map(memo => ({ memo })) : []; - const rows = [ - { - user_agent: userAgent, - usage_context: usageContext, - memos: memos, - account: account.xAddress, - amount: amount, - sequence: sequence, - }, - ]; - const bigquery = new BigQuery( - { - projectId: projectId, - credentials:{ - client_email: clientEmail, - private_key: privateKey, - } - } - ); - - bigquery - .dataset(datasetId) - .table(tableId) - .insert(rows, (error) => { - if (error) { - console.warn("WARNING: Failed to insert into BigQuery", JSON.stringify(error, null, 2)); - } else { - console.log(`Inserted ${rows.length} rows`); - } - }); - console.log("inserted big query") - } - - /// prepare res - if (!req.body.destination) { - response.balance = Number(amount) - } - res.send(response) - txCount++ - } else if (result.engine_result === 'tefPAST_SEQ' || result.engine_result === 'terPRE_SEQ') { - // occurs when we re-connect to a different rippled server - //??? - console.log(`${reqId}| Failed to fund ${account.address} with ${amount} XRP (${result.engine_result})`) - res.status(503).send({ - error: 'Failed to fund account. Try again later', - account - }) - - // advance cached sequence if needed: - getSequenceFromAccountInfo({reqId, shouldAdvanceSequence: false}) - } else { - console.log(`${reqId}| Unrecognized failure to fund ${account.address} with ${amount} XRP (${result.engine_result})`) - res.status(503).send({ - error: 'Failed to fund account', - account - }) - // TODO: Look for this in the logs - console.log(`${reqId}| Setting nextAvailableSeq=null`) - nextAvailableSeq = null - } - }).catch(err => { - console.log(`${reqId}| ${err}`) - // [DisconnectedError(websocket was closed)] - // from prepare* call - res.status(500).send({ - error: 'Unable to fund account. Server load is too high. Try again later', - account - }) - nextAvailableSeq = null - resetRippleAPI(reqId) - }) - } catch (e) { - console.log('/accounts error:', e) - res.status(500).send({ - error: 'Internal Server Error' - }) - } -}) - -// required: -// - options.reqId -// - options.shouldAdvanceSequence -function getSequenceFromAccountInfo(options) { - const reqId = options.reqId - - console.log(`${reqId}| (requesting account info...)`) - return api.request('account_info', { - account: address, - strict: true, - ledger_index: 'current', - queue: true - }).then(info => { - checkForWarning(info) - - let sequence - - sequence = info.account_data.Sequence - if (info.queue_data && info.queue_data.transactions && info.queue_data.transactions.length) { - const seqs = info.queue_data.transactions.reduce((acc, curr) => { - acc.push(curr.seq) - }, []) - seqs.sort((a, b) => a - b) // numeric sort, low to high - for (let i = 0; i < seqs.length; i++) { - if (sequence === seqs[i]) { - sequence++ - } else if (sequence < seqs[i]) { - console.log(`${reqId}| WARNING: found gap in Sequence: account_data.Sequence=${info.account_data.Sequence}, sequence=${sequence}, seqs[${i}]=${seqs[i]}`) - } else if (sequence > seqs[i]) { - console.log(`${reqId}| ERROR: invariant violated: account_data.Sequence=${info.account_data.Sequence}, sequence=${sequence}, seqs[${i}]=${seqs[i]}`) - } - } - } - - if (!nextAvailableSeq || nextAvailableSeq === sequence) { - if (options.shouldAdvanceSequence === true) { - // the sequence we found is the one we should use for this tx; - // sequence + 1 will be the one to use in the next tx - nextAvailableSeq = sequence + 1 - } - } else if (nextAvailableSeq > sequence) { - console.log(`${reqId}| WARNING: nextAvailableSeq=${nextAvailableSeq} > sequence=${sequence}. Some prior tx likely was not applied. Setting nextAvailableSeq=${options.shouldAdvanceSequence ? sequence : sequence + 1}.`) - nextAvailableSeq = sequence - // TODO: consider setting nextAvailableSeq=null - if (options.shouldAdvanceSequence === true) { - // sequence = nextAvailableSeq - nextAvailableSeq++ - } - } else if (nextAvailableSeq < sequence) { - console.log(`${reqId}| WARNING: nextAvailableSeq=${nextAvailableSeq} < sequence=${sequence}. Another process/server is using this funding account, or we were disconnected and reconnected to a different rippled server`) - nextAvailableSeq = sequence - if (options.shouldAdvanceSequence === true) { - nextAvailableSeq++ - } - } - console.log(`${reqId}| called account_info; sequence: ${sequence}, account_data.Sequence=${info.account_data.Sequence}, queue_data.transactions.length=${info.queue_data && info.queue_data.transactions && info.queue_data.transactions.length}`) - - return sequence - }) -} - -app.get('/info', (req, res) => { - createRippleAPI() - api.connect().then(() => { - return api.getServerInfo() - }).then(info => { - checkForWarning(info) - - return api.request('fee').then(fee => { - checkForWarning(fee) - - return api.request('account_info', { - account: address, - strict: true, - ledger_index: 'current', - queue: true - }).then(account => { - console.log('Returning /info - ledgerVersion: ' + info.validatedLedger.ledgerVersion + ', age: ' + info.validatedLedger.age + ', expected_ledger_size: ' + fee.expected_ledger_size + ', open_ledger_fee: ' + fee.drops.open_ledger_fee + ', hostID: ' + info.hostID) - const processUptime = process.uptime() - const osUptime = os.uptime() - res.send({ - faucetVersion: '0.0.2', - processUptime, - processUptimeHhMmSs: format(processUptime), - osUptime, - osUptimeHhMmSs: format(osUptime), - balance: account.account_data.Balance, - rippled: info, - fee - }) - }) - }) - }).catch(e => { - console.log('/info error:', e) - // [DisconnectedError(websocket was closed)] - res.status(500).send({ - error: 'Server load is too high. Request info later' - }) - nextAvailableSeq = null - resetRippleAPI('info') - }) -}) - -function format(seconds){ - function pad(s){ - return (s < 10 ? '0' : '') + s; - } - var hours = Math.floor(seconds / (60*60)); - var minutes = Math.floor(seconds % (60*60) / 60); - var seconds = Math.floor(seconds % 60); - - return pad(hours) + ':' + pad(minutes) + ':' + pad(seconds); -} - -app.get('/status', (req, res) => { - createRippleAPI() - api.connect().then(() => { - return api.getServerInfo() - }).then(info => { - checkForWarning(info) - - return api.request('fee').then(fee => { - checkForWarning(fee) - - console.log('Returning /status - ledgerVersion: ' + info.validatedLedger.ledgerVersion + ', age: ' + info.validatedLedger.age + ', expected_ledger_size: ' + fee.expected_ledger_size + ', open_ledger_fee: ' + fee.drops.open_ledger_fee + ', hostID: ' + info.hostID) - const processUptime = process.uptime() - -// broken - showing 0 -// Since startup, I have received *${txRequestCount} requests* and sent *${txCount} transactions*. - - const text = `*XRP Test Net Faucet:* https://xrpl.org/xrp-testnet-faucet.html -*Uptime:* ${format(processUptime)} -*rippled* buildVersion: ${info.buildVersion} -> completeLedgers: ${info.completeLedgers} -> loadFactor: ${info.loadFactor} -> peers: ${info.peers} -> serverState: ${info.serverState} -> validatedLedger.ledgerVersion: ${info.validatedLedger.ledgerVersion} -> hostID: ${info.hostID} -> open_ledger_fee: ${fee.drops.open_ledger_fee} drops -> expected_ledger_size: ${fee.expected_ledger_size} -Full info: https://faucet.altnet.rippletest.net/info` - res.send({ - "response_type": "in_channel", - "text": text -}) - }) - }).catch(e => { - console.log('/status error:', e) - // [DisconnectedError(websocket was closed)] - res.status(500).send({ - error: 'Server load is too high. Request status later' - }) - nextAvailableSeq = null - resetRippleAPI('info') - }) -}) - -const server = app.listen(port, () => console.log(`Altnet faucet, node version: ${process.version}, listening on port: ${port}`)) -server.setTimeout(20 * 1000) - -// Report TPS every minute -let peak = 0 -let peakRequests = 0 -setInterval(() => { - if (txCount > peak) { - peak = txCount - } - if (txRequestCount > peakRequests) { - peakRequests = txRequestCount - } - console.log(`[TPS] success=${txCount}, tps=${(txCount / 60).toFixed(1)}, peak=${peak}, requests=${txRequestCount}, rps=${(txRequestCount / 60).toFixed(1)}, peakRequests=${peakRequests}, success%=${((txCount / txRequestCount) * 100).toFixed(1)}%, success_peak/request_peak=${((peak / peakRequests) * 100).toFixed(1)}%`) - txCount = 0 - txRequestCount = 0 -}, 60 * 1000) diff --git a/src/config.ts b/src/config.ts index e6bfec1..59d9bfc 100644 --- a/src/config.ts +++ b/src/config.ts @@ -47,8 +47,6 @@ required.forEach((field: string) => { }); (new Map(Object.entries(defaults))).forEach((value: string, key: any) => { - console.log(value, key) - // const [key, value] = defaultOption[] if(!(key in config)) { config[key] = value } diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index 05fb2c5..e260203 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -69,7 +69,6 @@ export default async function (req: Request, res: Response) { amount: Number(amount), }; - // TODO: check for tefNO_TICKET and try again with another ticket if (status === "tesSUCCESS" || status === "terQUEUED") { console.log( `${rTracer.id()} | Funded ${ From 4f5598f21d9249de0dbbaaa2019a9bf7f05913dc Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 25 Jul 2023 14:32:24 -0700 Subject: [PATCH 08/26] fix client.ts according to comments --- src/client.ts | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/client.ts b/src/client.ts index d62ba77..30e36fe 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,8 +1,7 @@ import { Client } from "xrpl"; import { config } from "./config"; -let client = new Client(config.RIPPLED_URI); -let clientCreatedDate: number | null = Date.now(); +let { client, clientCreatedDate: _clientCreatedDate } = createClient(); client.on("error", (errorCode, errorMessage) => { console.log("Client error: " + errorCode + ": " + errorMessage); @@ -18,49 +17,63 @@ client.on("disconnected", (code) => { console.log("Client disconnected, code:", code); }); -export function createClient(): Client { - if (client) { - return client; +export function createClient(existingClient?: Client): { + client: Client; + clientCreatedDate: number; +} { + let createdDate: number; + let client: Client; + + if (existingClient) { + client = existingClient; + createdDate = _clientCreatedDate; + } else { + client = new Client(config.RIPPLED_URI); + createdDate = Date.now(); } - client = new Client(config.RIPPLED_URI); - clientCreatedDate = Date.now(); - return client; + + return { client, clientCreatedDate: createdDate }; } -export function getClientCreationDate(): number | null { - return clientCreatedDate; +export function getClientCreatedDate(): number { + return _clientCreatedDate; } -export async function connect(): Promise { +export async function connect(client: Client): Promise { if (!client.isConnected()) { await client.connect(); } return client; } -export async function disconnect(): Promise { +export async function disconnect(client: Client): Promise { await client.disconnect(); } + export const MIN_RESET_INTERVAL_MS = 10 * 1000; export async function resetClient(reqId: string) { - const clientCreationDate = getClientCreationDate(); + const clientCreationDate = getClientCreatedDate(); if (clientCreationDate) { const clientAge = Date.now() - clientCreationDate; if (clientAge < MIN_RESET_INTERVAL_MS) { console.log( - `${reqId}| not resetting client, age=${clientAge} ms < 10 sec` + `${reqId}| not resetting client, age=${clientAge} ms < ${ + MIN_RESET_INTERVAL_MS / 1000 + } sec` ); - return; // prevent client from being reset more often than once per 10 sec + return; // prevent client from being reset more often than once per MIN_RESET_INTERVAL_MS / 1000 sec } } console.log(`${reqId}| resetting client...`); - await disconnect(); + await disconnect(client); console.log(`${reqId}| successfully disconnected 'by user'`); - await createClient(); - await connect(); + const newClientData = await createClient(); + client = newClientData.client; + _clientCreatedDate = newClientData.clientCreatedDate; + await connect(client); console.log(`${reqId}| client reconnected.`); } From 23e42a20204fe7fb287934d8f96e28bdd1a3f3ff Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 25 Jul 2023 14:33:57 -0700 Subject: [PATCH 09/26] cleaning up client.ts --- src/client.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client.ts b/src/client.ts index 30e36fe..b35a435 100644 --- a/src/client.ts +++ b/src/client.ts @@ -39,14 +39,14 @@ export function getClientCreatedDate(): number { return _clientCreatedDate; } -export async function connect(client: Client): Promise { +export async function connect(): Promise { if (!client.isConnected()) { await client.connect(); } return client; } -export async function disconnect(client: Client): Promise { +export async function disconnect(): Promise { await client.disconnect(); } @@ -68,12 +68,12 @@ export async function resetClient(reqId: string) { } console.log(`${reqId}| resetting client...`); - await disconnect(client); + await disconnect(); console.log(`${reqId}| successfully disconnected 'by user'`); const newClientData = await createClient(); client = newClientData.client; _clientCreatedDate = newClientData.clientCreatedDate; - await connect(client); + await connect(); console.log(`${reqId}| client reconnected.`); } From ab8d15324a8326ea3f97484e0ad5da24a3e014c1 Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 25 Jul 2023 14:54:06 -0700 Subject: [PATCH 10/26] checking for existing ticketquene --- src/ticket-queue.ts | 112 ++++++++++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 35 deletions(-) diff --git a/src/ticket-queue.ts b/src/ticket-queue.ts index dde6a60..1ca8f06 100644 --- a/src/ticket-queue.ts +++ b/src/ticket-queue.ts @@ -1,47 +1,89 @@ -import { Client, isCreatedNode, TicketCreate, TransactionMetadata, TxResponse } from 'xrpl'; -import { fundingWallet } from './wallet'; -import { Ticket } from 'xrpl/dist/npm/models/ledger'; -import { config } from './config'; -import rTracer from 'cls-rtracer'; +import { + Client, + isCreatedNode, + TicketCreate, + TransactionMetadata, + TxResponse, +} from "xrpl"; +import { fundingWallet } from "./wallet"; +import { Ticket } from "xrpl/dist/npm/models/ledger"; +import { config } from "./config"; +import rTracer from "cls-rtracer"; -const ticketQueue: number[] = [] +let ticketQueue: number[] = []; let createTicketsPromise: Promise>; +async function populateTicketQueue(client: Client) { + // Get account info + const response = await client.request({ + command: "account_objects", + account: fundingWallet.address, + type: "ticket", + }); + console.log("Available Tickets:", response.result.account_objects); + + // Populate ticketQueue with existing ticket sequence numbers + for (let ticketObject of response.result.account_objects) { + const ticket = ticketObject as Ticket; // Treat the AccountObject as a Ticket + ticketQueue.push(Number(ticket.TicketSequence)); + } +} + // TODO: Populate the ticket queue on disconnect or every once in a while to account for tickets used by other systems export async function getTicket(client: Client) { - if(ticketQueue.length < config.MIN_TICKET_COUNT && !createTicketsPromise) { - const ticketsToCreate = Math.min(140, config.MAX_TICKET_COUNT - ticketQueue.length); - console.log(`Creating ${ticketsToCreate} tickets. ${ticketQueue.length} tickets remaining.`) - createTicketsPromise = client.submitAndWait( - { - TransactionType: 'TicketCreate', - TicketCount: Math.min(140, config.MAX_TICKET_COUNT - ticketQueue.length), - Account: fundingWallet.address - }, - { - wallet: fundingWallet - } - ).then((response) => { - let ticketsCreated = 0; - // Populate the ticket queue with newly created tickets - (response.result.meta as TransactionMetadata).AffectedNodes.forEach((node: any) => { - if(isCreatedNode(node) && node.CreatedNode.LedgerEntryType === 'Ticket'){ - ticketsCreated++; - ticketQueue.push((node.CreatedNode.NewFields as Partial).TicketSequence) + // Check Available Tickets --------------------------------------------------- + await populateTicketQueue(client); + if (ticketQueue.length < config.MIN_TICKET_COUNT && !createTicketsPromise) { + const ticketsToCreate = Math.min( + 140, + config.MAX_TICKET_COUNT - ticketQueue.length + ); + console.log( + `Creating ${ticketsToCreate} tickets. ${ticketQueue.length} tickets remaining.` + ); + createTicketsPromise = client + .submitAndWait( + { + TransactionType: "TicketCreate", + TicketCount: Math.min( + 140, + config.MAX_TICKET_COUNT - ticketQueue.length + ), + Account: fundingWallet.address, + }, + { + wallet: fundingWallet, } + ) + .then((response) => { + let ticketsCreated = 0; + // Populate the ticket queue with newly created tickets + (response.result.meta as TransactionMetadata).AffectedNodes.forEach( + (node: any) => { + if ( + isCreatedNode(node) && + node.CreatedNode.LedgerEntryType === "Ticket" + ) { + ticketsCreated++; + ticketQueue.push( + (node.CreatedNode.NewFields as Partial).TicketSequence + ); + } + } + ); + console.log(`Created tickets. Tx: ${response.result.hash}`); + createTicketsPromise = null; }) - console.log(`Created tickets. Tx: ${response.result.hash}`); - createTicketsPromise = null - }).catch((response) => { - console.log(`Failed to create tickets. Tx: ${response.result.hash}`); - }) + .catch((response) => { + console.log(`Failed to create tickets. Tx: ${response.result.hash}`); + }); } - if(ticketQueue.length === 0){ - console.log(`${rTracer.id()} | Waiting for tickets to be created.`) - await createTicketsPromise - return ticketQueue.shift() + if (ticketQueue.length === 0) { + console.log(`${rTracer.id()} | Waiting for tickets to be created.`); + await createTicketsPromise; + return ticketQueue.shift(); } - return Promise.resolve(ticketQueue.shift()) + return Promise.resolve(ticketQueue.shift()); } From e8efcad296bc169d0e7a6ebdd965f5099ed8a102 Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 25 Jul 2023 15:25:29 -0700 Subject: [PATCH 11/26] check existing ticket count --- src/routes/accounts.ts | 1 - src/ticket-queue.ts | 10 ++++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index e260203..936d84d 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -63,7 +63,6 @@ export default async function (req: Request, res: Response) { fundingWallet ); const status = result.engine_result; - // ... handle the rest of the logic as you have it currently const response = { account, amount: Number(amount), diff --git a/src/ticket-queue.ts b/src/ticket-queue.ts index 1ca8f06..bdba9e1 100644 --- a/src/ticket-queue.ts +++ b/src/ticket-queue.ts @@ -11,7 +11,7 @@ import { config } from "./config"; import rTracer from "cls-rtracer"; let ticketQueue: number[] = []; -let createTicketsPromise: Promise>; +let createTicketsPromise: Promise> = null; async function populateTicketQueue(client: Client) { // Get account info @@ -19,8 +19,9 @@ async function populateTicketQueue(client: Client) { command: "account_objects", account: fundingWallet.address, type: "ticket", + limit: 300, }); - console.log("Available Tickets:", response.result.account_objects); + console.log("Available Tickets:", response.result.account_objects.length); // Populate ticketQueue with existing ticket sequence numbers for (let ticketObject of response.result.account_objects) { @@ -45,10 +46,7 @@ export async function getTicket(client: Client) { .submitAndWait( { TransactionType: "TicketCreate", - TicketCount: Math.min( - 140, - config.MAX_TICKET_COUNT - ticketQueue.length - ), + TicketCount: ticketsToCreate, Account: fundingWallet.address, }, { From de1ba5066a781b06319c34c6e0460c8393078040 Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 25 Jul 2023 15:58:49 -0700 Subject: [PATCH 12/26] add ticketqueue promise --- src/ticket-queue.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ticket-queue.ts b/src/ticket-queue.ts index bdba9e1..498d506 100644 --- a/src/ticket-queue.ts +++ b/src/ticket-queue.ts @@ -13,6 +13,8 @@ import rTracer from "cls-rtracer"; let ticketQueue: number[] = []; let createTicketsPromise: Promise> = null; +let populateTicketQueuePromise: Promise | null = null; + async function populateTicketQueue(client: Client) { // Get account info const response = await client.request({ @@ -23,6 +25,9 @@ async function populateTicketQueue(client: Client) { }); console.log("Available Tickets:", response.result.account_objects.length); + // Empty the ticket queue before refilling it + ticketQueue = []; + // Populate ticketQueue with existing ticket sequence numbers for (let ticketObject of response.result.account_objects) { const ticket = ticketObject as Ticket; // Treat the AccountObject as a Ticket @@ -33,7 +38,12 @@ async function populateTicketQueue(client: Client) { // TODO: Populate the ticket queue on disconnect or every once in a while to account for tickets used by other systems export async function getTicket(client: Client) { // Check Available Tickets --------------------------------------------------- - await populateTicketQueue(client); + if (!populateTicketQueuePromise) { + populateTicketQueuePromise = populateTicketQueue(client).then(() => { + populateTicketQueuePromise = null; + }); + } + await populateTicketQueuePromise; if (ticketQueue.length < config.MIN_TICKET_COUNT && !createTicketsPromise) { const ticketsToCreate = Math.min( 140, @@ -54,7 +64,6 @@ export async function getTicket(client: Client) { } ) .then((response) => { - let ticketsCreated = 0; // Populate the ticket queue with newly created tickets (response.result.meta as TransactionMetadata).AffectedNodes.forEach( (node: any) => { @@ -62,7 +71,6 @@ export async function getTicket(client: Client) { isCreatedNode(node) && node.CreatedNode.LedgerEntryType === "Ticket" ) { - ticketsCreated++; ticketQueue.push( (node.CreatedNode.NewFields as Partial).TicketSequence ); From 2cddc5efd876f1279075a9757ae9447d8084f982 Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 25 Jul 2023 16:29:48 -0700 Subject: [PATCH 13/26] clean up --- src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 541bb90..a582ebc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,7 +22,6 @@ const server = app.listen(config.PORT, () => { }); server.setTimeout(20 * 1000); -// In your index.js or wherever txCount is defined export let txCount = 0; export let txRequestCount = 0; export function incrementTxCount() { From a3979aa1a08b8197588a7d296ae103faea9b9788 Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 25 Jul 2023 16:47:30 -0700 Subject: [PATCH 14/26] add txCount tracking and resolve ticketqueue issue --- src/routes/accounts.ts | 4 +++- src/ticket-queue.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index 936d84d..d081d7b 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -7,8 +7,10 @@ import { BigQuery } from "@google-cloud/bigquery"; import { config } from "../config"; import { getTicket } from "../ticket-queue"; import rTracer from "cls-rtracer"; +import { incrementTxRequestCount, incrementTxCount } from "../index"; export default async function (req: Request, res: Response) { + incrementTxRequestCount(); const client = await connect(); let account; @@ -113,7 +115,7 @@ export default async function (req: Request, res: Response) { }); console.log("inserted big query"); } - + incrementTxCount(); res.send(response); } } catch (err) { diff --git a/src/ticket-queue.ts b/src/ticket-queue.ts index 498d506..277bf64 100644 --- a/src/ticket-queue.ts +++ b/src/ticket-queue.ts @@ -39,7 +39,7 @@ async function populateTicketQueue(client: Client) { export async function getTicket(client: Client) { // Check Available Tickets --------------------------------------------------- if (!populateTicketQueuePromise) { - populateTicketQueuePromise = populateTicketQueue(client).then(() => { + populateTicketQueuePromise = populateTicketQueue(client).finally(() => { populateTicketQueuePromise = null; }); } From b4cf88a9cc49b8af698f5896150baa01cf503a6a Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 25 Jul 2023 17:04:02 -0700 Subject: [PATCH 15/26] convert wallet to Account --- src/routes/accounts.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index d081d7b..9899119 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -2,6 +2,7 @@ import { Request, Response } from "express"; import { connect, resetClient } from "../client"; import { getDestinationWallet } from "../destination-wallet"; import { Client, Payment, Wallet, xrpToDrops } from "xrpl"; +import { Account } from "../types"; import { fundingWallet } from "../wallet"; import { BigQuery } from "@google-cloud/bigquery"; import { config } from "../config"; @@ -13,7 +14,7 @@ export default async function (req: Request, res: Response) { incrementTxRequestCount(); const client = await connect(); - let account; + let account: Account; if (req.body.destination) { try { @@ -25,7 +26,12 @@ export default async function (req: Request, res: Response) { }); } } else { - account = Wallet.generate(); + let wallet = Wallet.generate(); + account = { + xAddress: wallet.getXAddress(), + address: wallet.classicAddress, + classicAddress: wallet.classicAddress, + }; console.log(`${rTracer.id()} | Generated new account: ${account.address}`); } @@ -87,8 +93,7 @@ export default async function (req: Request, res: Response) { user_agent: userAgent, usage_context: usageContext, memos: memos, - account: - "xAddress" in account ? account.xAddress : account.getXAddress(), + account: account.xAddress, amount: amount, }, ]; From 68fa50fa3c86d6fc86c16917a275e90a17c1ad9b Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Thu, 27 Jul 2023 13:11:45 -0700 Subject: [PATCH 16/26] refactor connect method, and rename functions --- src/client.ts | 22 ++++++++++++++++++---- src/destination-wallet.ts | 2 +- src/routes/accounts.ts | 8 ++++---- src/routes/info.ts | 4 ++-- src/routes/status.ts | 4 ++-- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/client.ts b/src/client.ts index b35a435..4c35ac1 100644 --- a/src/client.ts +++ b/src/client.ts @@ -2,6 +2,7 @@ import { Client } from "xrpl"; import { config } from "./config"; let { client, clientCreatedDate: _clientCreatedDate } = createClient(); +let connecting = false; client.on("error", (errorCode, errorMessage) => { console.log("Client error: " + errorCode + ": " + errorMessage); @@ -39,9 +40,22 @@ export function getClientCreatedDate(): number { return _clientCreatedDate; } -export async function connect(): Promise { - if (!client.isConnected()) { - await client.connect(); +export async function getConnectedClient(): Promise { + // If the client is not defined, not connected, and not currently being connected + if (!client || (!client.isConnected() && !connecting)) { + connecting = true; + try { + if (!client) { + const clientInfo = await createClient(); + client = clientInfo.client; + _clientCreatedDate = clientInfo.clientCreatedDate; + } + if (!client.isConnected()) { + await client.connect(); + } + } finally { + connecting = false; + } } return client; } @@ -74,6 +88,6 @@ export async function resetClient(reqId: string) { const newClientData = await createClient(); client = newClientData.client; _clientCreatedDate = newClientData.clientCreatedDate; - await connect(); + await getConnectedClient(); console.log(`${reqId}| client reconnected.`); } diff --git a/src/destination-wallet.ts b/src/destination-wallet.ts index b55b7ce..16e9e09 100644 --- a/src/destination-wallet.ts +++ b/src/destination-wallet.ts @@ -1,7 +1,7 @@ import { classicAddressToXAddress, isValidClassicAddress, xAddressToClassicAddress } from 'ripple-address-codec'; import { Account } from './types'; -export function getDestinationWallet(address?: string): Account { +export function getDestinationAccount(address?: string): Account { if(!isValidClassicAddress(address)) { throw Error('Invalid destination') } diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index 9899119..cdc32c4 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -1,6 +1,6 @@ import { Request, Response } from "express"; -import { connect, resetClient } from "../client"; -import { getDestinationWallet } from "../destination-wallet"; +import { getConnectedClient, resetClient } from "../client"; +import { getDestinationAccount } from "../destination-wallet"; import { Client, Payment, Wallet, xrpToDrops } from "xrpl"; import { Account } from "../types"; import { fundingWallet } from "../wallet"; @@ -12,13 +12,13 @@ import { incrementTxRequestCount, incrementTxCount } from "../index"; export default async function (req: Request, res: Response) { incrementTxRequestCount(); - const client = await connect(); + const client = await getConnectedClient(); let account: Account; if (req.body.destination) { try { - account = getDestinationWallet(req.body.destination); + account = getDestinationAccount(req.body.destination); console.log(`${rTracer.id()} | User-specified destination: ${account}`); } catch { return res.status(400).send({ diff --git a/src/routes/info.ts b/src/routes/info.ts index c69db2b..73c19b0 100644 --- a/src/routes/info.ts +++ b/src/routes/info.ts @@ -1,12 +1,12 @@ const os = require("os"); import { Request, Response } from "express"; -import { connect, resetClient } from "../client"; +import { getConnectedClient, resetClient } from "../client"; import { fundingWallet } from "../wallet"; import { checkForWarning, format } from "../utils"; import { AccountInfoResponse, FeeResponse, ServerInfoResponse } from "xrpl"; import * as packageJson from "../../package.json"; export default async function (req: Request, res: Response) { - let client = await connect(); + let client = await getConnectedClient(); try { const serverInfo: ServerInfoResponse = await client.request({ command: "server_info", diff --git a/src/routes/status.ts b/src/routes/status.ts index a407112..09ff7cc 100644 --- a/src/routes/status.ts +++ b/src/routes/status.ts @@ -1,10 +1,10 @@ import { Request, Response } from "express"; -import { connect, resetClient } from "../client"; +import { getConnectedClient, resetClient } from "../client"; import { checkForWarning, format } from "../utils"; import { FeeResponse, ServerInfoResponse } from "xrpl"; export default async function (req: Request, res: Response) { - let client = await connect(); + let client = await getConnectedClient(); try { const serverInfo: ServerInfoResponse = await client.request({ command: "server_info", From a2db94d3317931dd7e9482543fd40d7c45d2182c Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Thu, 27 Jul 2023 13:30:07 -0700 Subject: [PATCH 17/26] add destination tag --- src/routes/accounts.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index cdc32c4..9f964f1 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -64,6 +64,10 @@ export default async function (req: Request, res: Response) { Destination: account.address, Sequence: 0, }; + if (typeof account.tag === "number") { + payment.DestinationTag = account.tag; + } + try { const result = await submitPaymentWithTicket( payment, From 0234f671a9f6f6e4c4fe6f5858f2d47c57e8189a Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Thu, 27 Jul 2023 13:43:09 -0700 Subject: [PATCH 18/26] better error handling --- src/routes/accounts.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index 9f964f1..201a663 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -69,11 +69,19 @@ export default async function (req: Request, res: Response) { } try { - const result = await submitPaymentWithTicket( - payment, - client, - fundingWallet - ); + let result; + try { + result = await submitPaymentWithTicket(payment, client, fundingWallet); + } catch (err) { + console.log(`${rTracer.id()} | Failed to submit payment: ${err}`); + res.status(500).send({ + error: "Unable to fund account. Try again later", + account, + }); + await resetClient(rTracer.id().toString()); + return; + } + const status = result.engine_result; const response = { account, @@ -130,10 +138,9 @@ export default async function (req: Request, res: Response) { } catch (err) { console.log(`${rTracer.id()}| ${err}`); res.status(500).send({ - error: "Unable to fund account. Server load is too high. Try again later", + error: "Internal Server Error", account, }); - await resetClient(rTracer.id().toString()); } } From fdd1c2cbf405a6999231d00b3c7124784167a772 Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Fri, 28 Jul 2023 12:04:48 -0700 Subject: [PATCH 19/26] refactors --- src/client.ts | 2 + src/routes/accounts.ts | 85 +++++++++++++++++++++++++----------------- src/routes/info.ts | 12 +----- src/routes/status.ts | 11 +----- src/ticket-queue.ts | 49 +++++++++++++----------- 5 files changed, 83 insertions(+), 76 deletions(-) diff --git a/src/client.ts b/src/client.ts index 4c35ac1..f7e6a99 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,5 +1,6 @@ import { Client } from "xrpl"; import { config } from "./config"; +import { populateTicketQueue } from "./ticket-queue"; let { client, clientCreatedDate: _clientCreatedDate } = createClient(); let connecting = false; @@ -52,6 +53,7 @@ export async function getConnectedClient(): Promise { } if (!client.isConnected()) { await client.connect(); + await populateTicketQueue(client); } } finally { connecting = false; diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index 201a663..b103305 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -96,41 +96,12 @@ export default async function (req: Request, res: Response) { ); if (config.BIGQUERY_PROJECT_ID) { - const { userAgent = "", usageContext = "" } = req.body; - const memos = req.body.memos - ? req.body.memos.map((memo: any) => ({ memo })) - : []; - const rows = [ - { - user_agent: userAgent, - usage_context: usageContext, - memos: memos, - account: account.xAddress, - amount: amount, - }, - ]; - const bigquery = new BigQuery({ - projectId: config.BIGQUERY_PROJECT_ID, - credentials: { - client_email: config.BIGQUERY_CLIENT_EMAIL, - private_key: config.BIGQUERY_PRIVATE_KEY, - }, - }); - - bigquery - .dataset(config.BIGQUERY_DATASET_ID) - .table(config.BIGQUERY_TABLE_ID) - .insert(rows, (error) => { - if (error) { - console.warn( - "WARNING: Failed to insert into BigQuery", - JSON.stringify(error, null, 2) - ); - } else { - console.log(`Inserted ${rows.length} rows`); - } - }); - console.log("inserted big query"); + try { + await insertIntoBigQuery(account, amount, req.body); + console.log("inserted big query"); + } catch (error) { + console.warn(`Failed to insert into BigQuery: ${error}`); + } } incrementTxCount(); res.send(response); @@ -143,6 +114,50 @@ export default async function (req: Request, res: Response) { }); } } +async function insertIntoBigQuery( + account: Account, + amount: string, + reqBody: any +): Promise { + const { userAgent = "", usageContext = "" } = reqBody; + const memos = reqBody.memos + ? reqBody.memos.map((memo: any) => ({ memo })) + : []; + const rows = [ + { + user_agent: userAgent, + usage_context: usageContext, + memos: memos, + account: account.xAddress, + amount: amount, + }, + ]; + const bigquery = new BigQuery({ + projectId: config.BIGQUERY_PROJECT_ID, + credentials: { + client_email: config.BIGQUERY_CLIENT_EMAIL, + private_key: config.BIGQUERY_PRIVATE_KEY, + }, + }); + + return new Promise((resolve, reject) => { + bigquery + .dataset(config.BIGQUERY_DATASET_ID) + .table(config.BIGQUERY_TABLE_ID) + .insert(rows, (error) => { + if (error) { + console.warn( + "WARNING: Failed to insert into BigQuery", + JSON.stringify(error, null, 2) + ); + reject(error); + } else { + console.log(`Inserted ${rows.length} rows`); + resolve(); + } + }); + }); +} async function submitPaymentWithTicket( payment: Payment, diff --git a/src/routes/info.ts b/src/routes/info.ts index 73c19b0..47d3685 100644 --- a/src/routes/info.ts +++ b/src/routes/info.ts @@ -23,17 +23,9 @@ export default async function (req: Request, res: Response) { queue: true, }); console.log( - "Returning /info - ledgerVersion: " + - serverInfo.result.info.build_version + - ", age: " + - serverInfo.result.info.validated_ledger.age + - ", expected_ledger_size: " + - feeInfo.result.expected_ledger_size + - ", open_ledger_fee: " + - feeInfo.result.drops.open_ledger_fee + - ", hostID: " + - serverInfo.result.info.hostid + `Returning /info - ledgerVersion: ${serverInfo.result.info.build_version}, age: ${serverInfo.result.info.validated_ledger.age}, expected_ledger_size: ${feeInfo.result.expected_ledger_size}, open_ledger_fee: ${feeInfo.result.drops.open_ledger_fee}, hostID: ${serverInfo.result.info.hostid}` ); + const processUptime = process.uptime(); const osUptime = os.uptime(); res.send({ diff --git a/src/routes/status.ts b/src/routes/status.ts index 09ff7cc..718fd5e 100644 --- a/src/routes/status.ts +++ b/src/routes/status.ts @@ -16,16 +16,7 @@ export default async function (req: Request, res: Response) { }); checkForWarning(feeInfo.result); console.log( - "Returning /status - ledgerVersion: " + - serverInfo.result.info.validated_ledger.seq + - ", age: " + - serverInfo.result.info.validated_ledger.age + - ", expected_ledger_size: " + - feeInfo.result.expected_ledger_size + - ", open_ledger_fee: " + - feeInfo.result.drops.open_ledger_fee + - ", hostID: " + - serverInfo.result.info.hostid + `Returning /status - ledgerVersion: ${serverInfo.result.info.validated_ledger.seq}, age: ${serverInfo.result.info.validated_ledger.age}, expected_ledger_size: ${feeInfo.result.expected_ledger_size}, open_ledger_fee: ${feeInfo.result.drops.open_ledger_fee}, hostID: ${serverInfo.result.info.hostid}` ); const processUptime = process.uptime(); diff --git a/src/ticket-queue.ts b/src/ticket-queue.ts index 277bf64..fb74017 100644 --- a/src/ticket-queue.ts +++ b/src/ticket-queue.ts @@ -9,49 +9,48 @@ import { fundingWallet } from "./wallet"; import { Ticket } from "xrpl/dist/npm/models/ledger"; import { config } from "./config"; import rTracer from "cls-rtracer"; +import { AccountObjectsResponse } from "xrpl"; let ticketQueue: number[] = []; let createTicketsPromise: Promise> = null; -let populateTicketQueuePromise: Promise | null = null; - -async function populateTicketQueue(client: Client) { +// this will be called when client is connected +export async function populateTicketQueue(client: Client) { // Get account info - const response = await client.request({ + const responses: AccountObjectsResponse[] = await client.requestAll({ command: "account_objects", account: fundingWallet.address, type: "ticket", limit: 300, }); - console.log("Available Tickets:", response.result.account_objects.length); - // Empty the ticket queue before refilling it ticketQueue = []; // Populate ticketQueue with existing ticket sequence numbers - for (let ticketObject of response.result.account_objects) { - const ticket = ticketObject as Ticket; // Treat the AccountObject as a Ticket - ticketQueue.push(Number(ticket.TicketSequence)); - } + responses.forEach((response) => { + if (response.result.account_objects) { + for (let ticketObject of response.result.account_objects) { + const ticket = ticketObject as Ticket; + ticketQueue.push(Number(ticket.TicketSequence)); + } + } + }); + console.log("Available Tickets:", ticketQueue.length); } -// TODO: Populate the ticket queue on disconnect or every once in a while to account for tickets used by other systems export async function getTicket(client: Client) { // Check Available Tickets --------------------------------------------------- - if (!populateTicketQueuePromise) { - populateTicketQueuePromise = populateTicketQueue(client).finally(() => { - populateTicketQueuePromise = null; - }); - } - await populateTicketQueuePromise; if (ticketQueue.length < config.MIN_TICKET_COUNT && !createTicketsPromise) { const ticketsToCreate = Math.min( 140, config.MAX_TICKET_COUNT - ticketQueue.length ); console.log( - `Creating ${ticketsToCreate} tickets. ${ticketQueue.length} tickets remaining.` + `${rTracer.id()} | Creating ${ticketsToCreate} tickets. ${ + ticketQueue.length + } tickets remaining.` ); + let createdTickets = 0; createTicketsPromise = client .submitAndWait( { @@ -74,14 +73,19 @@ export async function getTicket(client: Client) { ticketQueue.push( (node.CreatedNode.NewFields as Partial).TicketSequence ); + createdTickets++; } } ); - console.log(`Created tickets. Tx: ${response.result.hash}`); + console.log( + `Created ${createdTickets} tickets. Tx: ${response.result.hash}` + ); createTicketsPromise = null; }) .catch((response) => { - console.log(`Failed to create tickets. Tx: ${response.result.hash}`); + console.log( + `Failed to create tickets. Tx: ${response.result.hash}, Status Code: ${response.result.status}` + ); }); } @@ -91,5 +95,8 @@ export async function getTicket(client: Client) { return ticketQueue.shift(); } - return Promise.resolve(ticketQueue.shift()); + const ticket = ticketQueue.shift(); + console.log(`${rTracer.id()} | Remaining Tickets: ${ticketQueue.length}`); + + return Promise.resolve(ticket); } From fa56b702ba8638d86cbe3b92f983aea1c7ee57ab Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Fri, 28 Jul 2023 12:07:24 -0700 Subject: [PATCH 20/26] nit --- src/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils.ts b/src/utils.ts index aa0d804..f4f7905 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -8,6 +8,7 @@ export function format(seconds: number) { return pad(hours) + ":" + pad(minutes) + ":" + pad(seconds); } + export function checkForWarning(s: any) { if (s && s.warning) { console.log("GOT WARNING: " + s.warning); From 42d61fadded861153b7c5c7c1030c2a4a0bee75c Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Mon, 31 Jul 2023 14:07:55 -0700 Subject: [PATCH 21/26] Update src/routes/accounts.ts Co-authored-by: Jackson Mills --- src/routes/accounts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index b103305..edd9ec6 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -26,7 +26,7 @@ export default async function (req: Request, res: Response) { }); } } else { - let wallet = Wallet.generate(); + const wallet = Wallet.generate(); account = { xAddress: wallet.getXAddress(), address: wallet.classicAddress, From dbfaec032485ac577085dfacdfcca4835d67d30f Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Mon, 31 Jul 2023 14:48:33 -0700 Subject: [PATCH 22/26] changes from PR comments --- package-lock.json | 2 +- package.json | 2 +- src/client.ts | 9 +++++++-- src/ticket-queue.ts | 1 - 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6c3af5c..c8b1656 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "testnet-faucet", - "version": "1.0.0", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 8250626..e295f8b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "testnet-faucet", - "version": "1.0.0", + "version": "2.0.0", "description": "", "main": "dist/src/index.js", "scripts": { diff --git a/src/client.ts b/src/client.ts index f7e6a99..3d3b9dd 100644 --- a/src/client.ts +++ b/src/client.ts @@ -5,6 +5,9 @@ import { populateTicketQueue } from "./ticket-queue"; let { client, clientCreatedDate: _clientCreatedDate } = createClient(); let connecting = false; +// connect the client and populate the ticket queue immediately after creation +getConnectedClient(); + client.on("error", (errorCode, errorMessage) => { console.log("Client error: " + errorCode + ": " + errorMessage); }); @@ -14,8 +17,6 @@ client.on("connected", () => { }); client.on("disconnected", (code) => { - // code - [close code](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) sent by the server - // will be 1000 if this was normal closure console.log("Client disconnected, code:", code); }); @@ -55,6 +56,9 @@ export async function getConnectedClient(): Promise { await client.connect(); await populateTicketQueue(client); } + } catch (error) { + console.error(`Failed to get connected client. Error: ${error.message}`); + throw error; } finally { connecting = false; } @@ -91,5 +95,6 @@ export async function resetClient(reqId: string) { client = newClientData.client; _clientCreatedDate = newClientData.clientCreatedDate; await getConnectedClient(); + await populateTicketQueue(client); // Populate the ticket queue after reconnection console.log(`${reqId}| client reconnected.`); } diff --git a/src/ticket-queue.ts b/src/ticket-queue.ts index fb74017..0edab66 100644 --- a/src/ticket-queue.ts +++ b/src/ticket-queue.ts @@ -92,7 +92,6 @@ export async function getTicket(client: Client) { if (ticketQueue.length === 0) { console.log(`${rTracer.id()} | Waiting for tickets to be created.`); await createTicketsPromise; - return ticketQueue.shift(); } const ticket = ticketQueue.shift(); From abfe6aaa38ed7d3fbd0ab5db7c4e9b5ceea80e20 Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 1 Aug 2023 09:59:32 -0700 Subject: [PATCH 23/26] fundWallets using both address and secrets --- src/wallet.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/wallet.ts b/src/wallet.ts index 3909bcd..9f867fd 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -1,4 +1,6 @@ -import { Wallet } from 'xrpl'; -import { config } from './config'; +import { Wallet } from "xrpl"; +import { config } from "./config"; -export const fundingWallet = Wallet.fromSecret(config.FUNDING_SECRET) +export const fundingWallet = Wallet.fromSeed(config.FUNDING_SECRET, { + masterAddress: config.FUNDING_ADDRESS, +}); From 789ffd0cefe170040119bf2f1f1f232915950f2a Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 1 Aug 2023 11:37:00 -0700 Subject: [PATCH 24/26] get rid of extra logic --- src/routes/accounts.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index edd9ec6..867c457 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -168,17 +168,13 @@ async function submitPaymentWithTicket( let retryCount = 0; let result; while (retryCount < maxRetries) { - try { - payment.TicketSequence = await getTicket(client); - result = (await client.submit(payment, { wallet: fundingWallet })).result; - if (result.engine_result === "tefNO_TICKET") { - retryCount++; - console.log(`Retrying transaction (${retryCount}/${maxRetries})`); - } else { - break; - } - } catch (error) { - throw error; // Let the outer try-catch handle other errors + payment.TicketSequence = await getTicket(client); + result = (await client.submit(payment, { wallet: fundingWallet })).result; + if (result.engine_result === "tefNO_TICKET") { + retryCount++; + console.log(`Retrying transaction (${retryCount}/${maxRetries})`); + } else { + break; } } From 6eb68d087f94c5fe8b7408a10bcb7e6248fb445c Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Tue, 1 Aug 2023 17:25:11 -0700 Subject: [PATCH 25/26] config optional interface refactor --- src/config.ts | 80 ++++++++++++++++++++---------------------- src/routes/accounts.ts | 2 +- 2 files changed, 40 insertions(+), 42 deletions(-) diff --git a/src/config.ts b/src/config.ts index 59d9bfc..f255634 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,57 +1,55 @@ -export interface Config { - NODE_ENV: "production" | "development" - PORT: string - RIPPLED_URI: string - FUNDING_ADDRESS: string - FUNDING_SECRET: string - XRP_AMOUNT: string - MAX_AMOUNT: string - MIN_TICKET_COUNT: number - MAX_TICKET_COUNT: number - - BIGQUERY_DATASET_ID?: string - BIGQUERY_TABLE_ID?: string - BIGQUERY_CLIENT_EMAIL?: string - BIGQUERY_PROJECT_ID?: string - BIGQUERY_PRIVATE_KEY?: string +import dotenv, { DotenvParseOutput } from "dotenv"; + +export interface ConfigFile { + NODE_ENV?: "production" | "development"; + PORT?: string; + RIPPLED_URI: string; + FUNDING_ADDRESS?: string; + FUNDING_SECRET: string; + XRP_AMOUNT?: string; + MAX_AMOUNT?: string; + MIN_TICKET_COUNT?: number; + MAX_TICKET_COUNT?: number; + + // Optional - See "defaults" for default values + BIGQUERY_DATASET_ID?: string; + BIGQUERY_TABLE_ID?: string; + BIGQUERY_CLIENT_EMAIL?: string; + BIGQUERY_PROJECT_ID?: string; + BIGQUERY_PRIVATE_KEY?: string; } -const required: (keyof Config)[] = [ - 'RIPPLED_URI', - 'FUNDING_SECRET' -] +export interface Config extends Required {} -const defaults: Partial, any>> = { - PORT: '3000', - XRP_AMOUNT: '10000', - MAX_AMOUNT: '1000000', +const required: (keyof ConfigFile)[] = ["RIPPLED_URI", "FUNDING_SECRET"]; + +const defaults: Partial, any>> = { + PORT: "3000", + XRP_AMOUNT: "10000", + MAX_AMOUNT: "1000000", MIN_TICKET_COUNT: 100, MAX_TICKET_COUNT: 240, -} - -const dotenv = require('dotenv') +}; -const result = dotenv.config() +const result = dotenv.config(); if (result.error) { - throw result.error + throw result.error; } -const config = result.parsed; +let config: DotenvParseOutput & typeof defaults = { + ...defaults, + ...result.parsed, +}; // Validate required configuration options -required.forEach((field: string) => { - if(!(field in config)) { - throw new Error(`Config property ${field} is required`) +required.forEach((field) => { + if (!(field in config)) { + throw new Error(`Config property ${field} is required`); } }); -(new Map(Object.entries(defaults))).forEach((value: string, key: any) => { - if(!(key in config)) { - config[key] = value - } -}) +// Ensure config is treated as fully resolved Config, not ConfigFile +const finalConfig: Config = config as Config; -export { - config -} +export { finalConfig as config }; diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index 867c457..a29a83f 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -47,7 +47,7 @@ export default async function (req: Request, res: Response) { let requestedAmountNumber = Number(req.body.xrpAmount); if ( requestedAmountNumber < 0 || - requestedAmountNumber > config.MAX_AMOUNT || + requestedAmountNumber > Number(config.MAX_AMOUNT) || typeof requestedAmountNumber !== "number" ) { return res.status(400).send({ From f01425aec3b675908ebd34b95a81a713a71cda59 Mon Sep 17 00:00:00 2001 From: jonathanlei Date: Thu, 3 Aug 2023 11:39:14 -0700 Subject: [PATCH 26/26] update contributing.md and standalone node --- CONTRIBUTING.md | 102 +++++++++++++++++++++++++++ README.md | 17 +++-- config/rippled.cfg | 172 +++++++++++++++++++++++++++++++++++++++++++++ src/utils.ts | 13 ++++ 4 files changed, 298 insertions(+), 6 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 config/rippled.cfg diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a06fff1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,102 @@ +# XRP Ledger Testnet Faucet + +Funds new Testnet accounts + +## Usage + +### Run the server: + +``` +npm install +NODE_ENV="production" PORT=3000 RIPPLED_URI="wss://s.altnet.rippletest.net:51233" FUNDING_ADDRESS=rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe FUNDING_SECRET= XRP_AMOUNT=10000 +npm start +``` + +### Fund a new account: + +``` +curl -X POST localhost:3000/accounts +``` + +### Funding new accounts multiple times + +``` +for i in `seq 1 100`; do curl -X POST localhost:3000/accounts; done +``` + +## Run time options + +Environment variables: + +- `XRP_AMOUNT`: The number of XRP to fund new accounts with. On the Testnet operated by Ripple, the current funding amount is 1,000 Testnet XRP. + +## Google BigQuery Integration + +This application logs and analyzes data using Google BigQuery. To use this feature, you need to provide the necessary BigQuery credentials through environment variables. + +### Run the server with BigQuery (Optional): + +Please replace `BIGQUERY_PROJECT_ID`, `BIGQUERY_CLIENT_EMAIL`, and `BIGQUERY_PRIVATE_KEY` with your actual project ID, client email, and private key. + +### BigQuery Environment Variables: + +- `BIGQUERY_PROJECT_ID`: The ID of your Google Cloud project. +- `BIGQUERY_CLIENT_EMAIL`: The email address of your service account. +- `BIGQUERY_PRIVATE_KEY`: The private key from your service account JSON key file. Be sure to include the full private key, including the header and footer. + +In case you are running this application in a trusted environment (like Google Cloud Platform), you don't need to provide the `BIGQUERY_CLIENT_EMAIL` and `BIGQUERY_PRIVATE_KEY`. The application will use Application Default Credentials (ADC) provided by the environment. + +``` + +Please adjust the details as per your application requirements. + + +``` + + +### how to run tests on Standalone Node +1. Creating a custom standalone rippled instance + +Create a folder called config + +Create a config file like xrpl.js uses in it’s CI for making standalone rippled instances: https://github.com/XRPLF/xrpl.js/blob/main/.ci-config/rippled.cfg + +If you want to change something, like increase the network_id, you can search the example config for the field name, then add it anywhere. For example: + +[network_id] +1234 + + +Go to right above config in the command line + +Use the config folder to start a docker container using a command like in xrpl.js tests. Source for command pre-modification + explanation of each piece. + +Modifications: + +Changed the path to the config folder from $PWD/.ci-config to $PWD/config:/config/ + +NOTE: This is pointing to a FOLDER not a file! + +Also updated the xrpllabsofficial version to be it’s generic form instead of specifically the beta (which is currently in use for xrpl.js) + +docker run -p 6006:6006 --interactive -t --volume $PWD/config:/config/ xrpllabsofficial -a --start + +You should now have a running docker container with your custom config! + +(If you were trying the network_id change, you should see it show up in the docker logs on startup!) + + +In ticket-queue.ts and account.ts, import `sendLedgerAccept` and `delayedLedgerAccept()` from utils.ts +``` +async function sendLedgerAccept(client: Client): Promise { + return client.connection.request({ command: "ledger_accept" }); +} +async function delayedLedgerAccept(): Promise { + await new Promise((resolve) => { + setTimeout(resolve, 1000); + }); + return sendLedgerAccept(client); + } + +``` +use `delayedLedgerAccept()` before `client.submitAndWait()` and `await sendLedgerAccept()` after `client.submit()` in order to close the ledger on a standalone node. diff --git a/README.md b/README.md index b5497d3..ea5f767 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,11 @@ Funds new Testnet accounts ### Run the server: -```` +``` npm install -NODE_ENV="production" PORT=3000 RIPPLED_URI="wss://s.altnet.rippletest.net:51233" FUNDING_ADDRESS=rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe FUNDING_SECRET= XRP_AMOUNT=10000 npm start -```` - -Do not run multiple instances of the Faucet application using the same funding address. Since the Faucet currently tracks the funding account's sequence number internally, a second instance of the Faucet would consume sequence numbers that the first instance considers to be available. This is a temporary error, though: clients can always retry, and retried requests will generally succeed. +NODE_ENV="production" PORT=3000 RIPPLED_URI="wss://s.altnet.rippletest.net:51233" FUNDING_ADDRESS=rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe FUNDING_SECRET= XRP_AMOUNT=10000 +npm start +``` ### Fund a new account: @@ -19,6 +18,11 @@ Do not run multiple instances of the Faucet application using the same funding a curl -X POST localhost:3000/accounts ``` +### Funding new accounts multiple times + +``` +for i in `seq 1 100`; do curl -X POST localhost:3000/accounts; done +``` ## Run time options @@ -26,7 +30,6 @@ Environment variables: - `XRP_AMOUNT`: The number of XRP to fund new accounts with. On the Testnet operated by Ripple, the current funding amount is 1,000 Testnet XRP. - ## Google BigQuery Integration This application logs and analyzes data using Google BigQuery. To use this feature, you need to provide the necessary BigQuery credentials through environment variables. @@ -42,8 +45,10 @@ Please replace `BIGQUERY_PROJECT_ID`, `BIGQUERY_CLIENT_EMAIL`, and `BIGQUERY_PRI - `BIGQUERY_PRIVATE_KEY`: The private key from your service account JSON key file. Be sure to include the full private key, including the header and footer. In case you are running this application in a trusted environment (like Google Cloud Platform), you don't need to provide the `BIGQUERY_CLIENT_EMAIL` and `BIGQUERY_PRIVATE_KEY`. The application will use Application Default Credentials (ADC) provided by the environment. + ``` Please adjust the details as per your application requirements. +``` diff --git a/config/rippled.cfg b/config/rippled.cfg new file mode 100644 index 0000000..65501bf --- /dev/null +++ b/config/rippled.cfg @@ -0,0 +1,172 @@ +[server] +port_rpc_admin_local +port_ws_public +port_ws_admin_local + +# port_peer +# port_ws_admin_local +# ssl_key = /etc/ssl/private/server.key +# ssl_cert = /etc/ssl/certs/server.crt + +# IPs must be 0.0.0.0 instead of 127.0.0.1 to be accessed outside the docker container + +[port_rpc_admin_local] +port = 5005 +ip = 0.0.0.0 +admin = 0.0.0.0 +protocol = http + +[port_ws_public] +port = 80 +ip = 0.0.0.0 +protocol = ws + +# [port_peer] +# port = 51235 +# ip = 0.0.0.0 +# protocol = peer + +[port_ws_admin_local] +port = 6006 +ip = 0.0.0.0 +admin = 0.0.0.0 +protocol = ws + +[node_size] +small + +# tiny +# small +# medium +# large +# huge + +[node_db] +type=NuDB +path=/var/lib/rippled/db/nudb +advisory_delete=0 + +# How many ledgers do we want to keep (history)? +# Integer value that defines the number of ledgers +# between online deletion events +online_delete=256 + +[ledger_history] +# How many ledgers do we want to keep (history)? +# Integer value (ledger count) +# or (if you have lots of TB SSD storage): 'full' +256 + +[database_path] +/var/lib/rippled/db + +[debug_logfile] +/var/log/rippled/debug.log + +[sntp_servers] +time.windows.com +time.apple.com +time.nist.gov +pool.ntp.org + +[ips] +r.ripple.com 51235 + +[validators_file] +validators.txt + +[network_id] +431431 + +[rpc_startup] +{ "command": "log_level", "severity": "info" } + +# severity (order: lots of information .. only errors) +# debug +# info +# warn +# error +# fatal + +[ssl_verify] +1 + +# The [features] stanza does not currently work for standalone mode: https://github.com/XRPLF/xrpl-dev-portal/issues/1762#issuecomment-1441252450 + + +# In order to enable an amendment which by default would vote "No", you must include its amendment id and name here. +# To add amendments specifically from the latest releases of rippled: +# 1. Go to https://xrpl.org/known-amendments.html +# 2. Find the first amendment in the latest releases of rippled which are not already in the list below +# 3. Click on each amendment to get their Amendment ID and name to add to this list manually. +# You will likely update the list with all amendments from a new release of rippled all at once. + +# To get the list of amendments on a network (e.g. devnet): +# 1. Run this ledger_entry command against the network to get a list of enabled amendment ids. (Command is in the websocket link as an easy way to run it) +# https://xrpl.org/websocket-api-tool.html?server=wss%3A%2F%2Fs1.ripple.com%2F&req=%7B%22command%22%3A%22ledger_entry%22%2C%22index%22%3A%227DB0788C020F02780A673DC74757F23823FA3014C1866E72CC4CD8B226CD6EF4%22%2C%22ledger_index%22%3A%22validated%22%7D +# 2. Strip away the quotes and commas +# 3. Add the amendment name to the same line as each amendment id (You can look them up via hash on https://xrpl.org/known-amendments.html) +# Ex. 4C97EBA926031A7CF7D7B36FDE3ED66DDA5421192D63DE53FFB46E43B9DC8373 Multisign +# The amendment name can be any string (including just a number) +# +# Note: The version of rippled you use this config with must have an implementation for the amendments you attempt to enable or it will crash. +# If you need the version of rippled to be more up to date, you may need to make a comment on this repo: https://github.com/WietseWind/docker-rippled + +[amendments] +# Devnet amendments as of June 28th, 2023 +B4E4F5D2D6FB84DF7399960A732309C9FD530EAE5941838160042833625A6076 NegativeUNL +DF8B4536989BDACE3F934F29423848B9F1D76D09BE6A1FCFE7E7F06AA26ABEAD fixRemoveNFTokenAutoTrustLine +3C43D9A973AA4443EF3FC38E42DD306160FBFFDAB901CD8BAA15D09F2597EB87 NonFungibleTokensV1 +98DECF327BF79997AEC178323AD51A830E457BFC6D454DAF3E46E5EC42DC619F CheckCashMakesTrustLine +B6B3EEDC0267AB50491FDC450A398AF30DBCD977CECED8BEF2499CAB5DAC19E2 fixRmSmallIncreasedQOffers +452F5906C46D46F407883344BFDD90E672B672C5E9943DB4891E3A34FEEEB9DB fixSTAmountCanonicalize +AF8DF7465C338AE64B1E937D6C8DA138C0D63AD5134A68792BBBE1F63356C422 FlowSortStrands +955DF3FA5891195A9DAEFA1DDC6BB244B545DDE1BAA84CBB25D5F12A8DA68A0C TicketBatch +B4D44CC3111ADD964E846FC57760C8B50FFCD5A82C86A72756F6B058DDDF96AD fix1201 +89308AF3B8B10B7192C4E613E1D2E4D9BA64B2EE2D5232402AE82A6A7220D953 fixQualityUpperBound +3012E8230864E95A58C60FD61430D7E1B4D3353195F2981DC12B0C7C0950FFAC FlowCross +DC9CA96AEA1DCF83E527D1AFC916EFAF5D27388ECA4060A88817C1238CAEE0BF EnforceInvariants +B9E739B8296B4A1BB29BE990B17D66E21B62A300A909F25AC55C22D6C72E1F9D fix1523 +1F4AFA8FA1BC8827AD4C0F682C03A8B671DCDF6B5C4DE36D44243A684103EF88 HardenedValidations +3CBC5C4E630A1B82380295CDA84B32B49DD066602E74E39B85EF64137FA65194 DepositPreauth +586480873651E106F1D6339B0C4A8945BA705A777F3F4524626FF1FC07EFE41D MultiSignReserve +58BE9B5968C4DA7C59BA900961828B113E5490699B21877DEF9A31E9D0FE5D5F fix1623 +42426C4D4F1009EE67080A9B7965B44656D7714D104A72F9B4369F97ABF044EE FeeEscalation +08DE7D96082187F6E6578530258C77FAABABE4C20474BDB82F04B021F1A68647 PayChan +67A34F2CF55BFC0F93AACD5B281413176FEE195269FA6D95219A2DF738671172 fix1513 +00C1FC4A53E60AB02C864641002B3172F38677E29C26C5406685179B37E1EDAC RequireFullyCanonicalSig +CA7C02118BA27599528543DFE77BA6838D1B0F43B447D4D7F53523CE6A0E9AC2 fix1543 +532651B4FD58DF8922A49BA101AB3E996E5BFBF95A913B3E392504863E63B164 TickSize +25BA44241B3BD880770BFA4DA21C7180576831855368CBEC6A3154FDE4A7676E fix1781 +8F81B066ED20DAECA20DF57187767685EEF3980B228E0667A650BAF24426D3B4 fixCheckThreading +5D08145F0A4983F23AFFFF514E83FAD355C5ABFBB6CAB76FB5BC8519FF5F33BE fix1515 +1562511F573A19AE9BD103B5D6B9E01B3B46805AEC5D3C4805C902B514399146 CryptoConditions +1D3463A5891F9E589C5AE839FFAC4A917CE96197098A1EF22304E1BC5B98A454 fix1528 +621A0B264970359869E3C0363A899909AAB7A887C8B73519E4ECF952D33258A8 fixPayChanRecipientOwnerDir +CC5ABAE4F3EC92E94A59B1908C2BE82D2228B6485C00AFF8F22DF930D89C194E SortedDirectories +FBD513F1B893AC765B78F250E6FFA6A11B573209D1842ADC787C850696741288 fix1578 +7117E2EC2DBF119CA55181D69819F1999ECEE1A0225A7FD2B9ED47940968479C fix1571 +4F46DF03559967AC60F2EB272FEFE3928A7594A45FF774B87A7E540DB0F8F068 fixAmendmentMajorityCalc +2CD5286D8D687E98B41102BDD797198E81EA41DF7BD104E6561FEB104EFF2561 fixTakerDryOfferRemoval +C4483A1896170C66C098DEA5B0E024309C60DC960DE5F01CD7AF986AA3D9AD37 fixMasterKeyAsRegularKey +740352F2412A9909880C23A559FCECEDA3BE2126FED62FC7660D628A06927F11 Flow +07D43DCE529B15A10827E5E04943B496762F9A88E3268269D69C44BE49E21104 Escrow +6781F8368C4771B83E8B821D88F580202BCB4228075297B19E4FDC5233F1EFDC TrustSetAuth +30CD365592B8EE40489BA01AE2F7555CAC9C983145871DC82A42A31CF5BAE7D9 DeletableAccounts +F64E1EABBE79D55B3BB82020516CEC2C582A98A6BFE20FBE9BB6A0D233418064 DepositAuth +E2E6F2866106419B88C50045ACE96368558C345566AC8F2BDF5A5B5587F0E6FA fix1368 +6C92211186613F9647A89DFFBAB8F94C99D4C7E956D495270789128569177DA1 fix1512 +42EEA5E28A97824821D4EF97081FE36A54E9593C6E4F20CBAE098C69D2E072DC fix1373 +4C97EBA926031A7CF7D7B36FDE3ED66DDA5421192D63DE53FFB46E43B9DC8373 MultiSign +157D2D480E006395B76F948E3E07A45A05FE10230D88A7993C71F97AE4B1F2D1 Checks +32A122F1352A4C7B3A6D790362CC34749C5E57FCE896377BFDC6CCD14F6CD627 NonFungibleTokensV1_1 +# 1.10.0 Amendments +47C3002ABA31628447E8E9A8B315FAA935CE30183F9A9B86845E469CA2CDC3DF DisallowIncoming +73761231F7F3D94EC3D8C63D91BDD0D89045C6F71B917D1925C01253515A6669 fixNonFungibleTokensV1_2 +F1ED6B4A411D8B872E65B9DCB4C8B100375B0DD3D62D07192E011D6D7F339013 fixTrustLinesToSelf +2E2FB9CF8A44EB80F4694D38AADAE9B8B7ADAFD2F092E10068E61C98C4F092B0 fixUniversalNumber +75A7E01C505DD5A179DFE3E000A9B6F1EDDEB55A12F95579A23E15B15DC8BE5A ImmediateOfferKilled +93E516234E35E08CA689FA33A6D38E103881F8DCB53023F728C307AA89D515A7 XRPFees +# 1.12.0-b1 Amendments +56B241D7A43D40354D02A9DC4C8DF5C7A1F930D92A9035C4E12291B3CA3E1C2B featureClawback +27CD95EE8E1E5A537FF2F89B6CEB7C622E78E9374EBD7DCBEDFAE21CD6F16E0A fixReducedOffersV1 \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index f4f7905..002b0fd 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,5 @@ +import { Client } from "xrpl"; + export function format(seconds: number) { function pad(s: number) { return (s < 10 ? "0" : "") + s; @@ -14,3 +16,14 @@ export function checkForWarning(s: any) { console.log("GOT WARNING: " + s.warning); } } + +// utils functions for standalone node +export async function sendLedgerAccept(client: Client): Promise { + return client.connection.request({ command: "ledger_accept" }); +} +export async function delayedLedgerAccept(client: Client): Promise { + await new Promise((resolve) => { + setTimeout(resolve, 1000); + }); + return sendLedgerAccept(client); +}