From 5a5119716a4c2a8524a3877734786f4a0706690d Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 15 Nov 2025 17:03:02 -0800 Subject: [PATCH] feat(slp): Ported SLP endpoints from bch-api --- .env-local | 5 +- package-lock.json | 1370 +++++++++++++++--- package.json | 2 + src/adapters/index.js | 2 + src/adapters/slp-indexer-api.js | 124 ++ src/config/env/common.js | 12 + src/controllers/rest-api/index.js | 4 + src/controllers/rest-api/slp/controller.js | 245 ++++ src/controllers/rest-api/slp/router.js | 56 + src/use-cases/index.js | 2 + src/use-cases/slp-use-cases.js | 333 +++++ test/unit/controllers/rest-api-index-unit.js | 15 + test/unit/controllers/slp-controller-unit.js | 369 +++++ test/unit/use-cases/slp-use-cases-unit.js | 311 ++++ 14 files changed, 2669 insertions(+), 181 deletions(-) create mode 100644 src/adapters/slp-indexer-api.js create mode 100644 src/controllers/rest-api/slp/controller.js create mode 100644 src/controllers/rest-api/slp/router.js create mode 100644 src/use-cases/slp-use-cases.js create mode 100644 test/unit/controllers/slp-controller-unit.js create mode 100644 test/unit/use-cases/slp-use-cases-unit.js diff --git a/.env-local b/.env-local index 66a0217..92b69cf 100644 --- a/.env-local +++ b/.env-local @@ -7,4 +7,7 @@ RPC_PASSWORD=password X402_ENABLED=false # Fulcrum Indexer -FULCRUM_API=http://192.168.2.127:3001 \ No newline at end of file +FULCRUM_API=http://192.168.2.127:3001 + +# SLP Indexer +SLP_INDEXER_API=http://192.168.2.127:5010 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index cb3c2f2..1c9eb0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,8 @@ "cors": "2.8.5", "dotenv": "16.3.1", "express": "5.1.0", + "minimal-slp-wallet": "5.13.3", + "slp-token-media": "1.2.10", "winston": "3.11.0", "winston-daily-rotate-file": "4.7.1", "x402-bch-express": "1.1.1" @@ -50,6 +52,16 @@ "node": ">=10.15.1" } }, + "node_modules/@chris.troutner/retry-queue-commonjs": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@chris.troutner/retry-queue-commonjs/-/retry-queue-commonjs-1.0.8.tgz", + "integrity": "sha512-jEHmCKffjIXTm0d/YcRQzCDPbzW2NFTEO72b1vmETnmATO57MmO39F37gEaq7J7lnF5JMxuM3Oq+FWa1L7HXxA==", + "license": "MIT", + "dependencies": { + "p-queue": "4.0.0", + "p-retry": "4.6.2" + } + }, "node_modules/@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", @@ -74,7 +86,6 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -87,7 +98,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -104,7 +114,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -121,7 +130,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -138,7 +146,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -155,7 +162,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -172,7 +178,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -189,7 +194,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -206,7 +210,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -223,7 +226,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -240,7 +242,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -257,7 +258,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -274,7 +274,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -291,7 +290,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -308,7 +306,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -325,7 +322,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -342,7 +338,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -359,7 +354,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -376,7 +370,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -393,7 +386,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -410,7 +402,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -427,7 +418,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -444,7 +434,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -672,7 +661,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -683,7 +671,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -693,7 +680,6 @@ "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -704,14 +690,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -971,7 +955,6 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "*", @@ -982,7 +965,6 @@ "version": "3.7.7", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, "license": "MIT", "dependencies": { "@types/eslint": "*", @@ -993,7 +975,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, "node_modules/@types/istanbul-lib-coverage": { @@ -1007,7 +988,6 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, "license": "MIT" }, "node_modules/@types/json5": { @@ -1021,12 +1001,17 @@ "version": "24.10.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" } }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, "node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", @@ -1044,7 +1029,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", @@ -1055,28 +1039,24 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", @@ -1088,14 +1068,12 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -1108,7 +1086,6 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" @@ -1118,7 +1095,6 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" @@ -1128,14 +1104,12 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -1152,7 +1126,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -1166,7 +1139,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -1179,7 +1151,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -1194,7 +1165,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -1205,7 +1175,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", - "dev": true, "license": "MIT", "peerDependencies": { "webpack": "4.x.x || 5.x.x", @@ -1216,7 +1185,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", - "dev": true, "license": "MIT", "dependencies": { "envinfo": "^7.7.3" @@ -1229,7 +1197,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", - "dev": true, "license": "MIT", "peerDependencies": { "webpack-cli": "4.x.x" @@ -1244,14 +1211,12 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, "license": "Apache-2.0" }, "node_modules/accepts": { @@ -1271,7 +1236,6 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -1284,7 +1248,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.13.0" @@ -1324,7 +1287,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -1342,7 +1304,6 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -1359,7 +1320,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, "license": "MIT" }, "node_modules/ansi-regex": { @@ -1392,7 +1352,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -1448,7 +1407,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { @@ -1694,7 +1652,6 @@ "version": "2.8.25", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz", "integrity": "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==", - "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -1709,6 +1666,539 @@ "node": ">=4.5.0" } }, + "node_modules/bch-consumer": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/bch-consumer/-/bch-consumer-1.6.2.tgz", + "integrity": "sha512-PclXVzmWtwqxe2ba6Mo9QbqxJ+5rUog8c+Ro5PV/W3IxkKNotVRa71bLei99AIELDO3A3ljfXkoCNsSM7tR37A==", + "license": "MIT", + "dependencies": { + "@psf/bch-js": "6.7.3", + "apidoc": "0.51.0", + "axios": "0.25.0" + } + }, + "node_modules/bch-consumer/node_modules/@psf/bch-js": { + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-6.7.3.tgz", + "integrity": "sha512-z6oJvPAXxSObPhqUAxCkCuPL2bp3Bs1oyhBZpQ5SR6cbJzcOMhuI4bHho2srH5agSCU+bbjehBMRGvo18eGW0A==", + "license": "MIT", + "dependencies": { + "@chris.troutner/bip32-utils": "1.0.5", + "@psf/bip21": "2.0.1", + "@psf/bitcoincash-ops": "2.0.0", + "@psf/bitcoincashjs-lib": "4.0.3", + "@psf/coininfo": "4.0.0", + "axios": "0.26.1", + "bc-bip68": "1.0.5", + "bchaddrjs-slp": "0.2.5", + "bigi": "1.4.2", + "bignumber.js": "9.0.0", + "bip-schnorr": "0.3.0", + "bip38": "2.0.2", + "bip39": "3.0.2", + "bip66": "1.1.5", + "bitcoinjs-message": "2.0.0", + "bs58": "4.0.1", + "ecashaddrjs": "1.0.7", + "ini": "1.3.8", + "randombytes": "2.0.6", + "safe-buffer": "5.1.2", + "satoshi-bitcoin": "1.0.4", + "slp-mdm": "0.0.6", + "slp-parser": "0.0.4", + "wif": "2.0.6" + } + }, + "node_modules/bch-consumer/node_modules/@psf/bch-js/node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, + "node_modules/bch-consumer/node_modules/apidoc": { + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.51.0.tgz", + "integrity": "sha512-3P4srhm6NA+kE/YRM4qL5jESElpQQJL+Z8n7hyr4uC+1DSwGzmE6adXPVxuT/hpDyShrqmRlHk8hf40RSqEsNw==", + "license": "MIT", + "os": [ + "darwin", + "freebsd", + "linux", + "openbsd", + "win32" + ], + "dependencies": { + "bootstrap": "3.4.1", + "commander": "^8.3.0", + "diff-match-patch": "^1.0.5", + "esbuild-loader": "^2.16.0", + "expose-loader": "^3.1.0", + "fs-extra": "^10.0.0", + "glob": "^7.2.0", + "handlebars": "^4.7.7", + "iconv-lite": "^0.6.3", + "jquery": "^3.6.0", + "klaw-sync": "^6.0.0", + "lodash": "^4.17.21", + "markdown-it": "^12.2.0", + "nodemon": "^2.0.15", + "path-to-regexp": "^6.2.0", + "prismjs": "^1.25.0", + "semver": "^7.3.5", + "style-loader": "^3.3.1", + "url-parse": "^1.5.3", + "webpack": "^5.64.2", + "webpack-cli": "^4.9.1", + "winston": "^3.3.3" + }, + "bin": { + "apidoc": "bin/apidoc" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/bch-consumer/node_modules/axios": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.7" + } + }, + "node_modules/bch-consumer/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/bch-consumer/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/bch-consumer/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/bch-consumer/node_modules/expose-loader": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-3.1.0.tgz", + "integrity": "sha512-2RExSo0yJiqP+xiUue13jQa2IHE8kLDzTI7b6kn+vUlBVvlzNSiLDzo4e5Pp5J039usvTUnxZ8sUOhv0Kg15NA==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/bch-consumer/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/bch-consumer/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bch-consumer/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/bch-consumer/node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/bch-consumer/node_modules/nodemon/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/bch-consumer/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" + }, + "node_modules/bch-consumer/node_modules/randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/bch-consumer/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/bch-consumer/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/bch-consumer/node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "license": "MIT", + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/bch-consumer/node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/bch-consumer/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bch-donation": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/bch-donation/-/bch-donation-1.1.2.tgz", + "integrity": "sha512-V7xQ23M6Ocavb1Nj4/pATwaLX/uF/9s2nWir3f+F3aetD3kMmsRHFRuXJwGQG3dSC5fJtRR/M5Z2zynY3t91Xg==", + "license": "MIT", + "dependencies": { + "apidoc": "0.51.0" + } + }, + "node_modules/bch-donation/node_modules/apidoc": { + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.51.0.tgz", + "integrity": "sha512-3P4srhm6NA+kE/YRM4qL5jESElpQQJL+Z8n7hyr4uC+1DSwGzmE6adXPVxuT/hpDyShrqmRlHk8hf40RSqEsNw==", + "license": "MIT", + "os": [ + "darwin", + "freebsd", + "linux", + "openbsd", + "win32" + ], + "dependencies": { + "bootstrap": "3.4.1", + "commander": "^8.3.0", + "diff-match-patch": "^1.0.5", + "esbuild-loader": "^2.16.0", + "expose-loader": "^3.1.0", + "fs-extra": "^10.0.0", + "glob": "^7.2.0", + "handlebars": "^4.7.7", + "iconv-lite": "^0.6.3", + "jquery": "^3.6.0", + "klaw-sync": "^6.0.0", + "lodash": "^4.17.21", + "markdown-it": "^12.2.0", + "nodemon": "^2.0.15", + "path-to-regexp": "^6.2.0", + "prismjs": "^1.25.0", + "semver": "^7.3.5", + "style-loader": "^3.3.1", + "url-parse": "^1.5.3", + "webpack": "^5.64.2", + "webpack-cli": "^4.9.1", + "winston": "^3.3.3" + }, + "bin": { + "apidoc": "bin/apidoc" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/bch-donation/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/bch-donation/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/bch-donation/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/bch-donation/node_modules/expose-loader": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-3.1.0.tgz", + "integrity": "sha512-2RExSo0yJiqP+xiUue13jQa2IHE8kLDzTI7b6kn+vUlBVvlzNSiLDzo4e5Pp5J039usvTUnxZ8sUOhv0Kg15NA==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/bch-donation/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/bch-donation/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bch-donation/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/bch-donation/node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/bch-donation/node_modules/nodemon/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/bch-donation/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" + }, + "node_modules/bch-donation/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/bch-donation/node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "license": "MIT", + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/bch-donation/node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/bch-donation/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/bchaddrjs-slp": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/bchaddrjs-slp/-/bchaddrjs-slp-0.2.5.tgz", @@ -1741,7 +2231,6 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, "license": "MIT", "engines": { "node": "*" @@ -1765,7 +2254,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1886,7 +2374,6 @@ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==", "deprecated": "This version of Bootstrap is no longer supported. Please upgrade to the latest version.", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -1906,7 +2393,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -1946,7 +2432,6 @@ "version": "4.27.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2009,7 +2494,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, "license": "MIT" }, "node_modules/buffer-xor": { @@ -2145,7 +2629,6 @@ "version": "1.0.30001754", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz", "integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2218,7 +2701,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0" @@ -2257,7 +2739,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4", @@ -2347,7 +2828,6 @@ "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, "license": "MIT" }, "node_modules/combined-stream": { @@ -2474,7 +2954,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -2485,6 +2964,12 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz", + "integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg==", + "license": "MIT" + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -2680,7 +3165,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", - "dev": true, "license": "Apache-2.0" }, "node_modules/doctrine": { @@ -2793,7 +3277,6 @@ "version": "1.5.249", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.249.tgz", "integrity": "sha512-5vcfL3BBe++qZ5kuFhD/p8WOM1N9m3nwvJPULJx+4xf2usSlZFJ0qoNYO2fOX4hi3ocuDcmDobtA+5SFr4OmBg==", - "dev": true, "license": "ISC" }, "node_modules/elliptic": { @@ -2822,7 +3305,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -2847,7 +3329,6 @@ "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", - "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -2861,7 +3342,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, "license": "BSD-2-Clause", "funding": { "url": "https://github.com/fb55/entities?sponsor=1" @@ -2871,7 +3351,6 @@ "version": "7.20.0", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.20.0.tgz", "integrity": "sha512-+zUomDcLXsVkQ37vUqWBvQwLaLlj8eZPSi61llaEFAVBY5mhcXdaSw1pSJVl4yTYD5g/gEfpNl28YYk4IPvrrg==", - "dev": true, "license": "MIT", "bin": { "envinfo": "dist/cli.js" @@ -3028,7 +3507,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { @@ -3092,7 +3570,6 @@ "version": "0.16.17", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.17.tgz", "integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -3130,7 +3607,6 @@ "version": "2.21.0", "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.21.0.tgz", "integrity": "sha512-k7ijTkCT43YBSZ6+fBCW1Gin7s46RrJ0VQaM8qA7lq7W+OLsGgtLyFV8470FzYi/4TeDexniTBTPTwZUnXXR5g==", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.16.17", @@ -3151,7 +3627,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3661,7 +4136,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -3674,7 +4148,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -3699,11 +4172,16 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.x" @@ -3782,7 +4260,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { @@ -3803,7 +4280,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "dev": true, "funding": [ { "type": "github", @@ -3820,7 +4296,6 @@ "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4.9.1" @@ -3874,7 +4349,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -3921,7 +4395,6 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, "license": "BSD-3-Clause", "bin": { "flat": "cli.js" @@ -4087,7 +4560,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4269,7 +4741,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, "license": "BSD-2-Clause" }, "node_modules/globals": { @@ -4320,7 +4791,6 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -4334,7 +4804,6 @@ "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, "license": "MIT", "dependencies": { "minimist": "^1.2.5", @@ -4385,7 +4854,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4603,7 +5071,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true, "license": "ISC" }, "node_modules/import-fresh": { @@ -4627,7 +5094,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, "license": "MIT", "dependencies": { "pkg-dir": "^4.2.0", @@ -4694,7 +5160,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.10" @@ -4787,7 +5252,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -4876,7 +5340,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4930,7 +5393,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -4967,7 +5429,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -5013,7 +5474,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, "license": "MIT", "dependencies": { "isobject": "^3.0.1" @@ -5199,14 +5659,12 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5289,7 +5747,6 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -5304,7 +5761,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -5320,7 +5776,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -5330,7 +5785,6 @@ "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", - "dev": true, "license": "MIT" }, "node_modules/js-sha256": { @@ -5377,7 +5831,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { @@ -5398,7 +5851,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -5411,7 +5863,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -5465,7 +5916,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5475,7 +5925,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", - "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.1.11" @@ -5505,7 +5954,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dev": true, "license": "MIT", "dependencies": { "uc.micro": "^1.0.1" @@ -5542,7 +5990,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.11.5" @@ -5556,7 +6003,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, "license": "MIT", "dependencies": { "big.js": "^5.2.2", @@ -5587,7 +6033,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, "license": "MIT" }, "node_modules/lodash.merge": { @@ -5671,7 +6116,6 @@ "version": "12.3.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1", @@ -5708,7 +6152,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true, "license": "MIT" }, "node_modules/media-typer": { @@ -5736,7 +6179,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, "license": "MIT" }, "node_modules/merkle-lib": { @@ -5766,6 +6208,300 @@ "node": ">= 0.6" } }, + "node_modules/minimal-slp-wallet": { + "version": "5.13.3", + "resolved": "https://registry.npmjs.org/minimal-slp-wallet/-/minimal-slp-wallet-5.13.3.tgz", + "integrity": "sha512-CwDipejJ64EGvCn0VohMXD8+4zX6lDE8KvYd7CHgmk+cT5HwQxO0WfGGL0m7HgWPpbDw5Eus/b6NXFQtRJIlxA==", + "license": "MIT", + "dependencies": { + "@chris.troutner/retry-queue-commonjs": "1.0.8", + "@psf/bch-js": "6.8.1", + "apidoc": "0.51.0", + "bch-consumer": "1.6.2", + "bch-donation": "1.1.2", + "crypto-js": "4.0.0" + } + }, + "node_modules/minimal-slp-wallet/node_modules/@psf/bch-js": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-6.8.1.tgz", + "integrity": "sha512-xu5YT9L3OhdILwJUmkV7Pg/WZ3r2aA6jD7fr5WMvo0OJSzzBBbXGShIbQNZp9+Vv9BoeVLCMqpZoW1Fp7OLCbg==", + "license": "MIT", + "dependencies": { + "@chris.troutner/bip32-utils": "1.0.5", + "@psf/bip21": "2.0.1", + "@psf/bitcoincash-ops": "2.0.0", + "@psf/bitcoincashjs-lib": "4.0.3", + "@psf/coininfo": "4.0.0", + "axios": "0.26.1", + "bc-bip68": "1.0.5", + "bchaddrjs-slp": "0.2.5", + "bigi": "1.4.2", + "bignumber.js": "9.0.0", + "bip-schnorr": "0.3.0", + "bip38": "2.0.2", + "bip39": "3.0.2", + "bip66": "1.1.5", + "bitcoinjs-message": "2.0.0", + "bs58": "4.0.1", + "ecashaddrjs": "1.0.7", + "ini": "1.3.8", + "randombytes": "2.0.6", + "safe-buffer": "5.1.2", + "satoshi-bitcoin": "1.0.4", + "slp-mdm": "0.0.6", + "slp-parser": "0.0.4", + "wif": "2.0.6" + } + }, + "node_modules/minimal-slp-wallet/node_modules/apidoc": { + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.51.0.tgz", + "integrity": "sha512-3P4srhm6NA+kE/YRM4qL5jESElpQQJL+Z8n7hyr4uC+1DSwGzmE6adXPVxuT/hpDyShrqmRlHk8hf40RSqEsNw==", + "license": "MIT", + "os": [ + "darwin", + "freebsd", + "linux", + "openbsd", + "win32" + ], + "dependencies": { + "bootstrap": "3.4.1", + "commander": "^8.3.0", + "diff-match-patch": "^1.0.5", + "esbuild-loader": "^2.16.0", + "expose-loader": "^3.1.0", + "fs-extra": "^10.0.0", + "glob": "^7.2.0", + "handlebars": "^4.7.7", + "iconv-lite": "^0.6.3", + "jquery": "^3.6.0", + "klaw-sync": "^6.0.0", + "lodash": "^4.17.21", + "markdown-it": "^12.2.0", + "nodemon": "^2.0.15", + "path-to-regexp": "^6.2.0", + "prismjs": "^1.25.0", + "semver": "^7.3.5", + "style-loader": "^3.3.1", + "url-parse": "^1.5.3", + "webpack": "^5.64.2", + "webpack-cli": "^4.9.1", + "winston": "^3.3.3" + }, + "bin": { + "apidoc": "bin/apidoc" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/minimal-slp-wallet/node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, + "node_modules/minimal-slp-wallet/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/minimal-slp-wallet/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/minimal-slp-wallet/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/minimal-slp-wallet/node_modules/expose-loader": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-3.1.0.tgz", + "integrity": "sha512-2RExSo0yJiqP+xiUue13jQa2IHE8kLDzTI7b6kn+vUlBVvlzNSiLDzo4e5Pp5J039usvTUnxZ8sUOhv0Kg15NA==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimal-slp-wallet/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/minimal-slp-wallet/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minimal-slp-wallet/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimal-slp-wallet/node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/minimal-slp-wallet/node_modules/nodemon/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/minimal-slp-wallet/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" + }, + "node_modules/minimal-slp-wallet/node_modules/randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/minimal-slp-wallet/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/minimal-slp-wallet/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/minimal-slp-wallet/node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "license": "MIT", + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/minimal-slp-wallet/node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/minimal-slp-wallet/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -5971,7 +6707,6 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, "license": "MIT" }, "node_modules/node-addon-api": { @@ -5995,7 +6730,6 @@ "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, "license": "MIT" }, "node_modules/nodemon": { @@ -6105,7 +6839,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6352,16 +7085,276 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-4.0.0.tgz", + "integrity": "sha512-3cRXXn3/O0o3+eVmUroJPSj/esxoEFIm0ZOno/T+NzG/VZgPOqQ8WKmlNqubSEpZmCIngEy34unkHGg83ZIBmg==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^3.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/p2wdb": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/p2wdb/-/p2wdb-2.2.10.tgz", + "integrity": "sha512-6X/SgkO2FSBlL5i0GWqEeuORQ2APji7w8ufkH2xR4YFJ9GtChcaV8Jdk6dNwVaJa+9bT3vPGwpFx93rttDxV8g==", + "license": "MIT", + "dependencies": { + "@chris.troutner/retry-queue-commonjs": "1.0.8", + "apidoc": "0.52.0", + "axios": "0.24.0" + } + }, + "node_modules/p2wdb/node_modules/apidoc": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.52.0.tgz", + "integrity": "sha512-k0gaMI7LWxaqKt2D+twC6XpI8X5SHkJalfo8TmF72d2TSNABquqB8LyIrVYzLcadcHuLMRFDaDibOK271gD67w==", + "license": "MIT", + "os": [ + "darwin", + "freebsd", + "linux", + "openbsd", + "win32" + ], + "dependencies": { + "bootstrap": "3.4.1", + "commander": "^8.3.0", + "diff-match-patch": "^1.0.5", + "esbuild-loader": "^2.16.0", + "expose-loader": "^3.1.0", + "fs-extra": "^10.0.0", + "glob": "^7.2.0", + "handlebars": "^4.7.7", + "iconv-lite": "^0.6.3", + "jquery": "^3.6.0", + "klaw-sync": "^6.0.0", + "lodash": "^4.17.21", + "markdown-it": "^12.2.0", + "nodemon": "^2.0.15", + "prismjs": "^1.25.0", + "semver": "^7.3.5", + "style-loader": "^3.3.1", + "webpack": "^5.64.2", + "webpack-cli": "^4.9.1", + "winston": "^3.3.3" + }, + "bin": { + "apidoc": "bin/apidoc" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/p2wdb/node_modules/axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.4" + } + }, + "node_modules/p2wdb/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/p2wdb/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/p2wdb/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/p2wdb/node_modules/expose-loader": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-3.1.0.tgz", + "integrity": "sha512-2RExSo0yJiqP+xiUue13jQa2IHE8kLDzTI7b6kn+vUlBVvlzNSiLDzo4e5Pp5J039usvTUnxZ8sUOhv0Kg15NA==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/p2wdb/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/p2wdb/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/p2wdb/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p2wdb/node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/p2wdb/node_modules/nodemon/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/p2wdb/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/p2wdb/node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "license": "MIT", + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/p2wdb/node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/p2wdb/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -6409,7 +7402,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6428,7 +7420,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6488,14 +7479,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -6598,7 +7587,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, "license": "MIT", "dependencies": { "find-up": "^4.0.0" @@ -6611,7 +7599,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "license": "MIT", "dependencies": { "locate-path": "^5.0.0", @@ -6625,7 +7612,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "license": "MIT", "dependencies": { "p-locate": "^4.1.0" @@ -6638,7 +7624,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "license": "MIT", "dependencies": { "p-try": "^2.0.0" @@ -6654,7 +7639,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "license": "MIT", "dependencies": { "p-limit": "^2.2.0" @@ -6686,7 +7670,6 @@ "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -6733,7 +7716,6 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true, "license": "MIT" }, "node_modules/punycode": { @@ -6761,6 +7743,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6879,7 +7867,6 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, "license": "MIT", "dependencies": { "resolve": "^1.9.0" @@ -6957,17 +7944,21 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.1", @@ -6988,7 +7979,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, "license": "MIT", "dependencies": { "resolve-from": "^5.0.0" @@ -7001,7 +7991,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7017,6 +8006,15 @@ "node": ">=4" } }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -7207,7 +8205,6 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dev": true, "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", @@ -7227,7 +8224,6 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -7244,7 +8240,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" @@ -7257,7 +8252,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, "license": "MIT" }, "node_modules/scryptsy": { @@ -7290,7 +8284,6 @@ "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -7325,7 +8318,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" @@ -7422,7 +8414,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, "license": "MIT", "dependencies": { "kind-of": "^6.0.2" @@ -7435,7 +8426,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -7448,7 +8438,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7579,6 +8568,26 @@ "bignumber.js": "^9.0.0" } }, + "node_modules/slp-mutable-data": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/slp-mutable-data/-/slp-mutable-data-2.3.10.tgz", + "integrity": "sha512-lyqY1gSjHQqLfJq1dDZUS7dTmYUTTk3j6Z4HXDAQ4X6vDsm7yYvXNKgHdiWueP0Jb0f1z1SJwJ/hZP6todch5g==", + "license": "GPL-2.0", + "dependencies": { + "axios": "0.27.2", + "p2wdb": "2.2.10" + } + }, + "node_modules/slp-mutable-data/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, "node_modules/slp-parser": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/slp-parser/-/slp-parser-0.0.4.tgz", @@ -7588,18 +8597,37 @@ "bignumber.js": "^9.0.0" } }, + "node_modules/slp-token-media": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/slp-token-media/-/slp-token-media-1.2.10.tgz", + "integrity": "sha512-+/QPMuLax9fN1aUYT+ftyx47P224OTQgJLXxzifB9ZC+m3rO5ItcOFC36naZDMTMzHYMld25FyU7N/1W3qfpPQ==", + "license": "MIT", + "dependencies": { + "@chris.troutner/retry-queue-commonjs": "1.0.8", + "axios": "0.27.2", + "slp-mutable-data": "2.3.10" + } + }, + "node_modules/slp-token-media/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true, "license": "MIT" }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -7609,7 +8637,6 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -7904,7 +8931,6 @@ "version": "3.3.4", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 12.13.0" @@ -7946,7 +8972,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8013,7 +9038,6 @@ "version": "5.44.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -8032,7 +9056,6 @@ "version": "5.3.14", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", @@ -8067,7 +9090,6 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, "license": "MIT" }, "node_modules/test-exclude": { @@ -8163,7 +9185,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -8185,7 +9206,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", - "dev": true, "license": "ISC", "bin": { "nodetouch": "bin/nodetouch.js" @@ -8360,14 +9380,12 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true, "license": "MIT" }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, "license": "BSD-2-Clause", "optional": true, "bin": { @@ -8399,21 +9417,18 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true, "license": "MIT" }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, "license": "MIT" }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -8432,7 +9447,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -8469,6 +9483,16 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -8522,7 +9546,6 @@ "version": "2.4.4", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", - "dev": true, "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", @@ -8536,7 +9559,6 @@ "version": "5.102.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz", "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", @@ -8585,7 +9607,6 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", - "dev": true, "license": "MIT", "dependencies": { "@discoveryjs/json-ext": "^0.5.0", @@ -8633,7 +9654,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 10" @@ -8643,7 +9663,6 @@ "version": "5.10.0", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "dev": true, "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", @@ -8658,7 +9677,6 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, "license": "MIT", "dependencies": { "source-list-map": "^2.0.0", @@ -8669,7 +9687,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -8683,7 +9700,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -8693,7 +9709,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -8703,7 +9718,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -8716,7 +9730,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.13.0" @@ -8726,7 +9739,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -8836,7 +9848,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true, "license": "MIT" }, "node_modules/winston": { @@ -8907,7 +9918,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, "license": "MIT" }, "node_modules/workerpool": { diff --git a/package.json b/package.json index 5386d53..28fe642 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "cors": "2.8.5", "dotenv": "16.3.1", "express": "5.1.0", + "minimal-slp-wallet": "5.13.3", + "slp-token-media": "1.2.10", "winston": "3.11.0", "winston-daily-rotate-file": "4.7.1", "x402-bch-express": "1.1.1" diff --git a/src/adapters/index.js b/src/adapters/index.js index f9b5c96..7d0d02c 100644 --- a/src/adapters/index.js +++ b/src/adapters/index.js @@ -8,6 +8,7 @@ // import NostrRelayAdapter from './nostr-relay.js' import FullNodeRPCAdapter from './full-node-rpc.js' import FulcrumAPIAdapter from './fulcrum-api.js' +import SlpIndexerAPIAdapter from './slp-indexer-api.js' import config from '../config/index.js' class Adapters { @@ -35,6 +36,7 @@ class Adapters { this.fullNode = new FullNodeRPCAdapter({ config: this.config }) this.fulcrum = new FulcrumAPIAdapter({ config: this.config }) + this.slpIndexer = new SlpIndexerAPIAdapter({ config: this.config }) } async start () { diff --git a/src/adapters/slp-indexer-api.js b/src/adapters/slp-indexer-api.js new file mode 100644 index 0000000..37fb721 --- /dev/null +++ b/src/adapters/slp-indexer-api.js @@ -0,0 +1,124 @@ +/* + Adapter library for interacting with SLP Indexer API service over HTTP. +*/ + +import axios from 'axios' +import wlogger from './wlogger.js' +import config from '../config/index.js' + +class SlpIndexerAPIAdapter { + constructor (localConfig = {}) { + this.config = localConfig.config || config + + // Allow missing config for testing environments + if (!this.config.slpIndexerApi || !this.config.slpIndexerApi.baseUrl) { + if (process.env.NODE_ENV === 'test' || process.env.TEST) { + // In test environment, create a mock baseURL + this.config.slpIndexerApi = { + baseUrl: 'http://localhost:5021', + timeoutMs: 15000 + } + } else { + throw new Error('SLP_INDEXER_API env var not set. Can not connect to PSF SLP indexer.') + } + } + + const { + baseUrl, + timeoutMs = 15000 + } = this.config.slpIndexerApi + + this.http = axios.create({ + baseURL: baseUrl, + timeout: timeoutMs + }) + } + + async get (path) { + try { + const response = await this.http.get(path) + return response.data + } catch (err) { + throw this._handleError(err) + } + } + + async post (path, data) { + try { + const response = await this.http.post(path, data) + return response.data + } catch (err) { + throw this._handleError(err) + } + } + + _handleError (err) { + const { status, message } = this.decodeError(err) + const error = new Error(message) + error.status = status + error.originalError = err + return error + } + + decodeError (err) { + try { + // Attempt to extract error message from response data + if (err.response && err.response.data) { + const data = err.response.data + // Handle structured error responses + if (data.error) { + return this._formatError(data.error, err.response.status || 400) + } + // Handle string error messages + if (typeof data === 'string') { + return this._formatError(data, err.response.status || 400) + } + // Handle object responses that might contain error info + if (typeof data === 'object' && data.message) { + return this._formatError(data.message, err.response.status || 400) + } + // Fallback to returning the status + return this._formatError('SLP Indexer API error', err.response.status || 500) + } + + // Network errors + if (err.message) { + if (err.message.includes('ENOTFOUND') || err.message.includes('ENETUNREACH') || err.message.includes('EAI_AGAIN')) { + return this._formatError( + 'Network error: Could not communicate with SLP Indexer API service.', + 503 + ) + } + } + + if (err.code && (err.code === 'ECONNABORTED' || err.code === 'ECONNREFUSED')) { + return this._formatError( + 'Network error: Could not communicate with SLP Indexer API service.', + 503 + ) + } + + if (err.error && typeof err.error === 'string' && err.error.includes('429')) { + return this._formatError('429 Too Many Requests', 429) + } + + if (err.message) { + return this._formatError(err.message, err.status || 422) + } + + return this._formatError('Unhandled SLP Indexer API error', 500) + } catch (decodeError) { + wlogger.error('Unhandled error in SlpIndexerAPIAdapter.decodeError()', decodeError) + return this._formatError('Internal server error', 500) + } + } + + _formatError (message, status = 500) { + return { + message: message || 'Internal server error', + status: status || 500 + } + } +} + +export default SlpIndexerAPIAdapter diff --git a/src/config/env/common.js b/src/config/env/common.js index 698fc6f..818e4b9 100644 --- a/src/config/env/common.js +++ b/src/config/env/common.js @@ -66,6 +66,18 @@ export default { timeoutMs: Number(process.env.FULCRUM_TIMEOUT_MS || 15000) }, + // SLP Indexer API configuration + slpIndexerApi: { + baseUrl: process.env.SLP_INDEXER_API || '', + timeoutMs: Number(process.env.SLP_INDEXER_TIMEOUT_MS || 15000) + }, + + // REST API URL for wallet operations + restURL: process.env.REST_URL || process.env.LOCAL_RESTURL || 'http://127.0.0.1:3000/v5/', + + // IPFS Gateway URL + ipfsGateway: process.env.IPFS_GATEWAY || 'p2wdb-gateway-678.fullstack.cash', + x402: x402Defaults, // Version diff --git a/src/controllers/rest-api/index.js b/src/controllers/rest-api/index.js index 2cfab3a..6c8127f 100644 --- a/src/controllers/rest-api/index.js +++ b/src/controllers/rest-api/index.js @@ -13,6 +13,7 @@ import DSProofRouter from './full-node/dsproof/router.js' import FulcrumRouter from './fulcrum/router.js' import MiningRouter from './full-node/mining/router.js' import RawTransactionsRouter from './full-node/rawtransactions/router.js' +import SlpRouter from './slp/router.js' import config from '../../config/index.js' class RESTControllers { @@ -76,6 +77,9 @@ class RESTControllers { const rawtransactionsRouter = new RawTransactionsRouter(dependencies) rawtransactionsRouter.attach(app) + + const slpRouter = new SlpRouter(dependencies) + slpRouter.attach(app) } } diff --git a/src/controllers/rest-api/slp/controller.js b/src/controllers/rest-api/slp/controller.js new file mode 100644 index 0000000..c77e1ef --- /dev/null +++ b/src/controllers/rest-api/slp/controller.js @@ -0,0 +1,245 @@ +/* + REST API Controller for the /slp routes. +*/ + +import wlogger from '../../../adapters/wlogger.js' +import BCHJS from '@psf/bch-js' + +const bchjs = new BCHJS() + +class SlpRESTController { + constructor (localConfig = {}) { + this.adapters = localConfig.adapters + if (!this.adapters) { + throw new Error( + 'Instance of Adapters library required when instantiating SLP REST Controller.' + ) + } + + this.useCases = localConfig.useCases + if (!this.useCases || !this.useCases.slp) { + throw new Error( + 'Instance of SLP use cases required when instantiating SLP REST Controller.' + ) + } + + this.slpUseCases = this.useCases.slp + + // Bind functions + this.root = this.root.bind(this) + this.getStatus = this.getStatus.bind(this) + this.getAddress = this.getAddress.bind(this) + this.getTxid = this.getTxid.bind(this) + this.getTokenStats = this.getTokenStats.bind(this) + this.getTokenData = this.getTokenData.bind(this) + this.getTokenData2 = this.getTokenData2.bind(this) + this.handleError = this.handleError.bind(this) + } + + /** + * @api {get} /v6/slp/ Service status + * @apiName SlpRoot + * @apiGroup SLP + * + * @apiDescription Returns the status of the SLP service. + * + * @apiSuccess {String} status Service identifier + */ + async root (req, res) { + return res.status(200).json({ status: 'psf-slp-indexer' }) + } + + /** + * Validates and converts an address to cash address format + * @param {string} address - Address to validate and convert + * @returns {string} Cash address + * @throws {Error} If address is invalid or not mainnet + */ + _validateAndConvertAddress (address) { + if (!address) { + throw new Error('address is empty') + } + + // Convert legacy to cash address + const cashAddr = bchjs.SLP.Address.toCashAddress(address) + + // Ensure it's a valid BCH address + try { + bchjs.SLP.Address.toLegacyAddress(cashAddr) + } catch (err) { + throw new Error(`Invalid BCH address. Double check your address is valid: ${address}`) + } + + // Ensure it's mainnet (no testnet support) + const isMainnet = bchjs.Address.isMainnetAddress(cashAddr) + if (!isMainnet) { + throw new Error('Invalid network. Only mainnet addresses are supported.') + } + + return cashAddr + } + + /** + * @api {get} /v6/slp/status Get indexer status + * @apiName GetStatus + * @apiGroup SLP + * @apiDescription Returns the status of the SLP indexer. + */ + async getStatus (req, res) { + try { + const result = await this.slpUseCases.getStatus() + return res.status(200).json(result) + } catch (err) { + return this.handleError(err, res) + } + } + + /** + * @api {post} /v6/slp/address Get SLP balance for address + * @apiName GetAddress + * @apiGroup SLP + * @apiDescription Returns SLP balance for an address. + */ + async getAddress (req, res) { + try { + const address = req.body.address + + if (!address || address === '') { + return res.status(400).json({ + success: false, + error: 'address can not be empty' + }) + } + + // Validate and convert address + const cashAddr = this._validateAndConvertAddress(address) + + const result = await this.slpUseCases.getAddress({ address: cashAddr }) + return res.status(200).json(result) + } catch (err) { + return this.handleError(err, res) + } + } + + /** + * @api {post} /v6/slp/txid Get SLP transaction data + * @apiName GetTxid + * @apiGroup SLP + * @apiDescription Returns SLP transaction data for a TXID. + */ + async getTxid (req, res) { + try { + const txid = req.body.txid + + if (!txid || txid === '') { + return res.status(400).json({ + success: false, + error: 'txid can not be empty' + }) + } + + if (txid.length !== 64) { + return res.status(400).json({ + success: false, + error: 'This is not a txid' + }) + } + + const result = await this.slpUseCases.getTxid({ txid }) + return res.status(200).json(result) + } catch (err) { + return this.handleError(err, res) + } + } + + /** + * @api {post} /v6/slp/token Get token statistics + * @apiName GetTokenStats + * @apiGroup SLP + * @apiDescription Returns statistics for a single SLP token. + */ + async getTokenStats (req, res) { + try { + const tokenId = req.body.tokenId + + if (!tokenId || tokenId === '') { + return res.status(400).json({ + success: false, + error: 'tokenId can not be empty' + }) + } + + // Flag to toggle tx history of the token + const withTxHistory = req.body.withTxHistory === true + + const result = await this.slpUseCases.getTokenStats({ tokenId, withTxHistory }) + return res.status(200).json(result) + } catch (err) { + return this.handleError(err, res) + } + } + + /** + * @api {post} /v6/slp/token/data Get token data + * @apiName GetTokenData + * @apiGroup SLP + * @apiDescription Get mutable and immutable data if the token contains them. + */ + async getTokenData (req, res) { + try { + const tokenId = req.body.tokenId + + if (!tokenId || tokenId === '') { + return res.status(400).json({ + success: false, + error: 'tokenId can not be empty' + }) + } + + // Flag to toggle tx history of the token + const withTxHistory = req.body.withTxHistory === true + + const result = await this.slpUseCases.getTokenData({ tokenId, withTxHistory }) + return res.status(200).json(result) + } catch (err) { + return this.handleError(err, res) + } + } + + /** + * @api {post} /v6/slp/token/data2 Get expanded token data + * @apiName GetTokenData2 + * @apiGroup SLP + * @apiDescription Get expanded data for the token, including icons. + */ + async getTokenData2 (req, res) { + try { + const tokenId = req.body.tokenId + + if (!tokenId || tokenId === '') { + return res.status(400).json({ + success: false, + error: 'tokenId can not be empty' + }) + } + + const updateCache = req.body.updateCache + + const result = await this.slpUseCases.getTokenData2({ tokenId, updateCache }) + return res.status(200).json(result) + } catch (err) { + return this.handleError(err, res) + } + } + + handleError (err, res) { + wlogger.error('Error in SlpRESTController:', err) + + const status = err.status || 500 + const message = err.message || 'Internal server error' + + return res.status(status).json({ error: message }) + } +} + +export default SlpRESTController diff --git a/src/controllers/rest-api/slp/router.js b/src/controllers/rest-api/slp/router.js new file mode 100644 index 0000000..9375812 --- /dev/null +++ b/src/controllers/rest-api/slp/router.js @@ -0,0 +1,56 @@ +/* + REST API router for /slp routes. +*/ + +import express from 'express' +import SlpRESTController from './controller.js' + +class SlpRouter { + constructor (localConfig = {}) { + this.adapters = localConfig.adapters + if (!this.adapters) { + throw new Error( + 'Instance of Adapters library required when instantiating SLP REST Router.' + ) + } + + this.useCases = localConfig.useCases + if (!this.useCases) { + throw new Error( + 'Instance of Use Cases library required when instantiating SLP REST Router.' + ) + } + + const dependencies = { + adapters: this.adapters, + useCases: this.useCases + } + + this.slpController = new SlpRESTController(dependencies) + + this.apiPrefix = (localConfig.apiPrefix || '').replace(/\/$/, '') + this.baseUrl = `${this.apiPrefix}/slp` + if (!this.baseUrl.startsWith('/')) { + this.baseUrl = `/${this.baseUrl}` + } + this.router = express.Router() + } + + attach (app) { + if (!app) { + throw new Error('Must pass app object when attaching REST API controllers.') + } + + this.router.get('/', this.slpController.root) + this.router.get('/status', this.slpController.getStatus) + this.router.post('/address', this.slpController.getAddress) + this.router.post('/txid', this.slpController.getTxid) + this.router.post('/token', this.slpController.getTokenStats) + this.router.post('/token/data', this.slpController.getTokenData) + this.router.post('/token/data2', this.slpController.getTokenData2) + + app.use(this.baseUrl, this.router) + } +} + +export default SlpRouter diff --git a/src/use-cases/index.js b/src/use-cases/index.js index bd4d60b..030a66a 100644 --- a/src/use-cases/index.js +++ b/src/use-cases/index.js @@ -11,6 +11,7 @@ import DSProofUseCases from './full-node-dsproof-use-cases.js' import FulcrumUseCases from './fulcrum-use-cases.js' import MiningUseCases from './full-node-mining-use-cases.js' import RawTransactionsUseCases from './full-node-rawtransactions-use-cases.js' +import SlpUseCases from './slp-use-cases.js' class UseCases { constructor (localConfig = {}) { @@ -27,6 +28,7 @@ class UseCases { this.fulcrum = new FulcrumUseCases({ adapters: this.adapters }) this.mining = new MiningUseCases({ adapters: this.adapters }) this.rawtransactions = new RawTransactionsUseCases({ adapters: this.adapters }) + this.slp = new SlpUseCases({ adapters: this.adapters }) } // Run any startup Use Cases at the start of the app. diff --git a/src/use-cases/slp-use-cases.js b/src/use-cases/slp-use-cases.js new file mode 100644 index 0000000..4a4555b --- /dev/null +++ b/src/use-cases/slp-use-cases.js @@ -0,0 +1,333 @@ +/* + Use cases for interacting with the SLP Indexer API service. +*/ + +import wlogger from '../adapters/wlogger.js' +import BCHJS from '@psf/bch-js' +import SlpWallet from 'minimal-slp-wallet' +import SlpTokenMedia from 'slp-token-media' +import axios from 'axios' +import config from '../config/index.js' + +const bchjs = new BCHJS() + +class SlpUseCases { + constructor (localConfig = {}) { + this.adapters = localConfig.adapters + + if (!this.adapters) { + throw new Error('Adapters instance required when instantiating SLP use cases.') + } + + this.slpIndexer = this.adapters.slpIndexer + if (!this.slpIndexer) { + throw new Error('SLP Indexer adapter required when instantiating SLP use cases.') + } + + // Allow bchjs to be injected for testing + this.bchjs = localConfig.bchjs || bchjs + + // Get config + this.config = localConfig.config || config + + // Initialize wallet (lazy initialization) + this.wallet = null + this.slpTokenMedia = null + this.walletInitialized = false + this.initializationPromise = null + } + + // Initialize wallet and SlpTokenMedia asynchronously + async _ensureInitialized () { + if (this.walletInitialized) { + return + } + + if (this.initializationPromise) { + return this.initializationPromise + } + + this.initializationPromise = this._initialize() + return this.initializationPromise + } + + async _initialize () { + try { + // Initialize wallet + this.wallet = new SlpWallet(undefined, { + restURL: this.config.restURL, + interface: 'rest-api' + }) + + // Wait for wallet to initialize + await this.wallet.walletInfoPromise + + // Initialize SlpTokenMedia + this.slpTokenMedia = new SlpTokenMedia({ + wallet: this.wallet, + ipfsGatewayUrl: this.config.ipfsGateway + }) + + this.walletInitialized = true + wlogger.info('SLP wallet and token media initialized') + } catch (err) { + wlogger.error('Error initializing SLP wallet:', err) + throw err + } + } + + async getStatus () { + try { + return await this.slpIndexer.get('slp/status/') + } catch (err) { + wlogger.error('Error in SlpUseCases.getStatus()', err) + throw err + } + } + + async getAddress ({ address }) { + try { + return await this.slpIndexer.post('slp/address/', { address }) + } catch (err) { + wlogger.error('Error in SlpUseCases.getAddress()', err) + throw err + } + } + + async getTxid ({ txid }) { + try { + return await this.slpIndexer.post('slp/tx/', { txid }) + } catch (err) { + wlogger.error('Error in SlpUseCases.getTxid()', err) + throw err + } + } + + async getTokenStats ({ tokenId, withTxHistory = false }) { + try { + return await this.slpIndexer.post('slp/token/', { tokenId, withTxHistory }) + } catch (err) { + wlogger.error('Error in SlpUseCases.getTokenStats()', err) + throw err + } + } + + async getTokenData ({ tokenId, withTxHistory = false }) { + try { + const tokenData = {} + + // Get token stats from the Genesis TX of the token + const response = await this.slpIndexer.post('slp/token/', { tokenId, withTxHistory }) + const tokenStats = response.tokenData + + tokenData.genesisData = tokenStats + + // Try to get immutable data + try { + const immutableData = tokenStats.documentUri + tokenData.immutableData = immutableData || '' + } catch (error) { + tokenData.immutableData = '' + } + + // Try to get mutable data + try { + const mutableData = await this.getMutableCid({ tokenStats }) + tokenData.mutableData = mutableData || '' + } catch (error) { + wlogger.warn('Error getting mutable data:', error) + tokenData.mutableData = '' + } + + return tokenData + } catch (err) { + wlogger.error('Error in SlpUseCases.getTokenData()', err) + throw err + } + } + + async getTokenData2 ({ tokenId, updateCache }) { + try { + await this._ensureInitialized() + + const tokenData = await this.slpTokenMedia.getIcon({ tokenId, updateCache }) + return tokenData + } catch (err) { + wlogger.error('Error in SlpUseCases.getTokenData2()', err) + throw err + } + } + + async getMutableCid ({ tokenStats }) { + // Validate input - this should throw, not be caught + if (!tokenStats || !tokenStats.documentHash) { + throw new Error('No documentHash property found in tokenStats') + } + + try { + await this._ensureInitialized() + + // Get the OP_RETURN data and decode it + const mutableData = await this.decodeOpReturn({ txid: tokenStats.documentHash }) + const jsonData = JSON.parse(mutableData) + + // mda = mutable data address + const mda = jsonData.mda + + // Get the mda transaction history + const transactions = await this.wallet.getTransactions(mda) + wlogger.info(`MDA has ${transactions.length} transactions in its history.`) + + const mdaTxs = transactions + + let data = false + + // These are used to filter blockchain data to find the most recent + // update to the MDA + let largestBlock = 700000 + let largestTimestamp = 1666107111271 + let bestEntry + + // Used to track the number of transactions before the best candidate is found + let txCnt = 0 + + // Map each transaction of the mda + // If it finds an OP_RETURN, decode it and exit the loop + for (let i = 0; i < mdaTxs.length; i++) { + const tx = mdaTxs[i] + const txid = tx.tx_hash + txCnt++ + + data = await this.decodeOpReturn({ txid }) + + // Try parse the OP_RETURN data to a JSON object + if (data) { + try { + // Convert the OP_RETURN data to a JSON object + const obj = JSON.parse(data) + + // Keep searching if this TX does not have a cid value + if (!obj.cid) continue + + // Ensure data was generated by the MDA + const txData = await this.wallet.getTxData([txid]) + const vinAddress = txData[0].vin[0].address + + // Skip entry if it was not made by the MDA private key + if (mda !== vinAddress) { + continue + } + + // First best entry found + if (!bestEntry) { + bestEntry = data + largestBlock = tx.height + + if (obj.ts) { + largestTimestamp = obj.ts + } + } else { + // One candidate already found. Looking for potentially better entry + + if (tx.height < largestBlock) { + // Exit loop if next candidate has an older block height + break + } + + if (obj.ts && obj.ts < largestTimestamp) { + // Continue looping through entries if the current entry in + // the same block has a smaller timestamp + continue + } + + bestEntry = data + largestBlock = tx.height + if (obj.ts) { + largestTimestamp = obj.ts + } + } + } catch (error) { + continue + } + } + } + + wlogger.info(`${txCnt} transactions reviewed to find mutable data.`) + + if (!bestEntry) { + return false + } + + // Get the CID + const obj = JSON.parse(bestEntry) + const cid = obj.cid + + if (!cid) { + return false + } + + // Assuming that CID starts with ipfs://. Cutting out that prefix + const mutableCid = cid.substring(7) + + return mutableCid + } catch (err) { + wlogger.error('Error in SlpUseCases.getMutableCid()', err) + return false + } + } + + async decodeOpReturn ({ txid }) { + try { + if (!txid || typeof txid !== 'string') { + throw new Error('txid must be a string.') + } + + // Get transaction data + const txData = await this.bchjs.Electrumx.txData(txid) + let data = false + + // Map the vout of the transaction in search of an OP_RETURN + for (let i = 0; i < txData.details.vout.length; i++) { + const vout = txData.details.vout[i] + + const script = this.bchjs.Script.toASM( + Buffer.from(vout.scriptPubKey.hex, 'hex') + ).split(' ') + + // Exit on the first OP_RETURN found + if (script[0] === 'OP_RETURN') { + data = Buffer.from(script[1], 'hex').toString('ascii') + break + } + } + + return data + } catch (error) { + wlogger.error('Error in SlpUseCases.decodeOpReturn()', error) + throw error + } + } + + async getCIDData ({ cid }) { + try { + if (!cid || typeof cid !== 'string') { + throw new Error('cid must be a string.') + } + + // Assuming that CID starts with ipfs://. Cutting out that prefix + const cidWithoutPrefix = cid.substring(7) + + const dataUrl = `https://${cidWithoutPrefix}.ipfs.dweb.link/data.json` + wlogger.info(`Fetching IPFS data from: ${dataUrl}`) + + const response = await axios.get(dataUrl) + + return response.data + } catch (error) { + wlogger.error('Error in SlpUseCases.getCIDData()', error) + throw error + } + } +} + +export default SlpUseCases diff --git a/test/unit/controllers/rest-api-index-unit.js b/test/unit/controllers/rest-api-index-unit.js index dcfa79d..6df2479 100644 --- a/test/unit/controllers/rest-api-index-unit.js +++ b/test/unit/controllers/rest-api-index-unit.js @@ -12,6 +12,7 @@ import DSProofRouter from '../../../src/controllers/rest-api/full-node/dsproof/r import MiningRouter from '../../../src/controllers/rest-api/full-node/mining/router.js' import RawTransactionsRouter from '../../../src/controllers/rest-api/full-node/rawtransactions/router.js' import FulcrumRouter from '../../../src/controllers/rest-api/fulcrum/router.js' +import SlpRouter from '../../../src/controllers/rest-api/slp/router.js' describe('#controllers/rest-api/index.js', () => { let sandbox @@ -84,6 +85,17 @@ describe('#controllers/rest-api/index.js', () => { getRawTransactions: () => {}, sendRawTransaction: () => {}, sendRawTransactions: () => {} + }, + slp: { + getStatus: () => {}, + getAddress: () => {}, + getTxid: () => {}, + getTokenStats: () => {}, + getTokenData: () => {}, + getTokenData2: () => {}, + getMutableCid: () => {}, + decodeOpReturn: () => {}, + getCIDData: () => {} } } }) @@ -116,6 +128,7 @@ describe('#controllers/rest-api/index.js', () => { const fulcrumAttachStub = sandbox.stub(FulcrumRouter.prototype, 'attach') const miningAttachStub = sandbox.stub(MiningRouter.prototype, 'attach') const rawtransactionsAttachStub = sandbox.stub(RawTransactionsRouter.prototype, 'attach') + const slpAttachStub = sandbox.stub(SlpRouter.prototype, 'attach') const restControllers = new RESTControllers({ adapters: mockAdapters, useCases: mockUseCases @@ -136,6 +149,8 @@ describe('#controllers/rest-api/index.js', () => { assert.equal(miningAttachStub.getCall(0).args[0], app) assert.isTrue(rawtransactionsAttachStub.calledOnce) assert.equal(rawtransactionsAttachStub.getCall(0).args[0], app) + assert.isTrue(slpAttachStub.calledOnce) + assert.equal(slpAttachStub.getCall(0).args[0], app) }) }) }) diff --git a/test/unit/controllers/slp-controller-unit.js b/test/unit/controllers/slp-controller-unit.js new file mode 100644 index 0000000..b86c345 --- /dev/null +++ b/test/unit/controllers/slp-controller-unit.js @@ -0,0 +1,369 @@ +/* + Unit tests for SlpRESTController. +*/ + +import { assert } from 'chai' +import sinon from 'sinon' + +import SlpRESTController from '../../../src/controllers/rest-api/slp/controller.js' +import { + createMockRequest, + createMockResponse +} from '../mocks/controller-mocks.js' + +// Valid mainnet cash address for testing +const VALID_MAINNET_ADDRESS = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' + +describe('#slp-controller.js', () => { + let sandbox + let mockUseCases + let mockAdapters + let uut + + const createSlpUseCaseStubs = () => ({ + getStatus: sandbox.stub().resolves({ status: 'ok' }), + getAddress: sandbox.stub().resolves({ balance: 1000 }), + getTxid: sandbox.stub().resolves({ txid: 'abc' }), + getTokenStats: sandbox.stub().resolves({ tokenData: {} }), + getTokenData: sandbox.stub().resolves({ genesisData: {}, immutableData: '', mutableData: '' }), + getTokenData2: sandbox.stub().resolves({ tokenIcon: 'test-icon.png' }) + }) + + beforeEach(() => { + sandbox = sinon.createSandbox() + mockAdapters = {} + mockUseCases = { + slp: createSlpUseCaseStubs() + } + + uut = new SlpRESTController({ + adapters: mockAdapters, + useCases: mockUseCases + }) + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('#constructor()', () => { + it('should require adapters', () => { + assert.throws(() => { + // eslint-disable-next-line no-new + new SlpRESTController({ useCases: mockUseCases }) + }, /Adapters library required/) + }) + + it('should require slp use cases', () => { + assert.throws(() => { + // eslint-disable-next-line no-new + new SlpRESTController({ adapters: mockAdapters, useCases: {} }) + }, /SLP use cases required/) + }) + }) + + describe('#root()', () => { + it('should return service status', async () => { + const req = createMockRequest() + const res = createMockResponse() + + await uut.root(req, res) + + assert.equal(res.statusValue, 200) + assert.deepEqual(res.jsonData, { status: 'psf-slp-indexer' }) + }) + }) + + describe('#getStatus()', () => { + it('should return status on success', async () => { + const req = createMockRequest() + const res = createMockResponse() + + await uut.getStatus(req, res) + + assert.equal(res.statusValue, 200) + assert.deepEqual(res.jsonData, { status: 'ok' }) + assert.isTrue(mockUseCases.slp.getStatus.calledOnce) + }) + + it('should handle errors via handleError', async () => { + const error = new Error('failure') + error.status = 503 + mockUseCases.slp.getStatus.rejects(error) + const req = createMockRequest() + const res = createMockResponse() + + await uut.getStatus(req, res) + + assert.equal(res.statusValue, 503) + assert.deepEqual(res.jsonData, { error: 'failure' }) + }) + }) + + describe('#getAddress()', () => { + it('should return address balance on success', async () => { + const req = createMockRequest({ + body: { address: VALID_MAINNET_ADDRESS } + }) + const res = createMockResponse() + + await uut.getAddress(req, res) + + assert.equal(res.statusValue, 200) + assert.deepEqual(res.jsonData, { balance: 1000 }) + assert.isTrue(mockUseCases.slp.getAddress.calledOnce) + }) + + it('should return error if address is empty', async () => { + const req = createMockRequest({ + body: { address: '' } + }) + const res = createMockResponse() + + await uut.getAddress(req, res) + + assert.equal(res.statusValue, 400) + assert.property(res.jsonData, 'error') + assert.include(res.jsonData.error, 'can not be empty') + }) + + it('should return error if address is missing', async () => { + const req = createMockRequest({ + body: {} + }) + const res = createMockResponse() + + await uut.getAddress(req, res) + + assert.equal(res.statusValue, 400) + assert.property(res.jsonData, 'error') + }) + + it('should handle errors via handleError', async () => { + const error = new Error('Invalid address') + error.status = 400 + mockUseCases.slp.getAddress.rejects(error) + const req = createMockRequest({ + body: { address: VALID_MAINNET_ADDRESS } + }) + const res = createMockResponse() + + await uut.getAddress(req, res) + + assert.equal(res.statusValue, 400) + assert.deepEqual(res.jsonData, { error: 'Invalid address' }) + }) + }) + + describe('#getTxid()', () => { + it('should return transaction data on success', async () => { + const req = createMockRequest({ + body: { txid: 'a'.repeat(64) } + }) + const res = createMockResponse() + + await uut.getTxid(req, res) + + assert.equal(res.statusValue, 200) + assert.deepEqual(res.jsonData, { txid: 'abc' }) + assert.isTrue(mockUseCases.slp.getTxid.calledOnce) + }) + + it('should return error if txid is empty', async () => { + const req = createMockRequest({ + body: { txid: '' } + }) + const res = createMockResponse() + + await uut.getTxid(req, res) + + assert.equal(res.statusValue, 400) + assert.property(res.jsonData, 'error') + assert.include(res.jsonData.error, 'can not be empty') + }) + + it('should return error if txid is not 64 characters', async () => { + const req = createMockRequest({ + body: { txid: 'abc' } + }) + const res = createMockResponse() + + await uut.getTxid(req, res) + + assert.equal(res.statusValue, 400) + assert.property(res.jsonData, 'error') + assert.include(res.jsonData.error, 'not a txid') + }) + + it('should handle errors via handleError', async () => { + const error = new Error('Transaction not found') + error.status = 404 + mockUseCases.slp.getTxid.rejects(error) + const req = createMockRequest({ + body: { txid: 'a'.repeat(64) } + }) + const res = createMockResponse() + + await uut.getTxid(req, res) + + assert.equal(res.statusValue, 404) + assert.deepEqual(res.jsonData, { error: 'Transaction not found' }) + }) + }) + + describe('#getTokenStats()', () => { + it('should return token stats on success', async () => { + const req = createMockRequest({ + body: { tokenId: 'a'.repeat(64) } + }) + const res = createMockResponse() + + await uut.getTokenStats(req, res) + + assert.equal(res.statusValue, 200) + assert.deepEqual(res.jsonData, { tokenData: {} }) + assert.isTrue(mockUseCases.slp.getTokenStats.calledOnce) + }) + + it('should pass withTxHistory flag', async () => { + const req = createMockRequest({ + body: { tokenId: 'a'.repeat(64), withTxHistory: true } + }) + const res = createMockResponse() + + await uut.getTokenStats(req, res) + + assert.isTrue(mockUseCases.slp.getTokenStats.calledWith({ + tokenId: 'a'.repeat(64), + withTxHistory: true + })) + }) + + it('should return error if tokenId is empty', async () => { + const req = createMockRequest({ + body: { tokenId: '' } + }) + const res = createMockResponse() + + await uut.getTokenStats(req, res) + + assert.equal(res.statusValue, 400) + assert.property(res.jsonData, 'error') + }) + + it('should handle errors via handleError', async () => { + const error = new Error('Token not found') + error.status = 404 + mockUseCases.slp.getTokenStats.rejects(error) + const req = createMockRequest({ + body: { tokenId: 'a'.repeat(64) } + }) + const res = createMockResponse() + + await uut.getTokenStats(req, res) + + assert.equal(res.statusValue, 404) + assert.deepEqual(res.jsonData, { error: 'Token not found' }) + }) + }) + + describe('#getTokenData()', () => { + it('should return token data on success', async () => { + const req = createMockRequest({ + body: { tokenId: 'a'.repeat(64) } + }) + const res = createMockResponse() + + await uut.getTokenData(req, res) + + assert.equal(res.statusValue, 200) + assert.property(res.jsonData, 'genesisData') + assert.property(res.jsonData, 'immutableData') + assert.property(res.jsonData, 'mutableData') + assert.isTrue(mockUseCases.slp.getTokenData.calledOnce) + }) + + it('should return error if tokenId is empty', async () => { + const req = createMockRequest({ + body: { tokenId: '' } + }) + const res = createMockResponse() + + await uut.getTokenData(req, res) + + assert.equal(res.statusValue, 400) + assert.property(res.jsonData, 'error') + }) + + it('should handle errors via handleError', async () => { + const error = new Error('Token data not found') + error.status = 404 + mockUseCases.slp.getTokenData.rejects(error) + const req = createMockRequest({ + body: { tokenId: 'a'.repeat(64) } + }) + const res = createMockResponse() + + await uut.getTokenData(req, res) + + assert.equal(res.statusValue, 404) + assert.deepEqual(res.jsonData, { error: 'Token data not found' }) + }) + }) + + describe('#getTokenData2()', () => { + it('should return expanded token data on success', async () => { + const req = createMockRequest({ + body: { tokenId: 'a'.repeat(64) } + }) + const res = createMockResponse() + + await uut.getTokenData2(req, res) + + assert.equal(res.statusValue, 200) + assert.deepEqual(res.jsonData, { tokenIcon: 'test-icon.png' }) + assert.isTrue(mockUseCases.slp.getTokenData2.calledOnce) + }) + + it('should pass updateCache flag', async () => { + const req = createMockRequest({ + body: { tokenId: 'a'.repeat(64), updateCache: true } + }) + const res = createMockResponse() + + await uut.getTokenData2(req, res) + + assert.isTrue(mockUseCases.slp.getTokenData2.calledWith({ + tokenId: 'a'.repeat(64), + updateCache: true + })) + }) + + it('should return error if tokenId is empty', async () => { + const req = createMockRequest({ + body: { tokenId: '' } + }) + const res = createMockResponse() + + await uut.getTokenData2(req, res) + + assert.equal(res.statusValue, 400) + assert.property(res.jsonData, 'error') + }) + + it('should handle errors via handleError', async () => { + const error = new Error('Token icon not found') + error.status = 404 + mockUseCases.slp.getTokenData2.rejects(error) + const req = createMockRequest({ + body: { tokenId: 'a'.repeat(64) } + }) + const res = createMockResponse() + + await uut.getTokenData2(req, res) + + assert.equal(res.statusValue, 404) + assert.deepEqual(res.jsonData, { error: 'Token icon not found' }) + }) + }) +}) diff --git a/test/unit/use-cases/slp-use-cases-unit.js b/test/unit/use-cases/slp-use-cases-unit.js new file mode 100644 index 0000000..9ab7a14 --- /dev/null +++ b/test/unit/use-cases/slp-use-cases-unit.js @@ -0,0 +1,311 @@ +/* + Unit tests for SlpUseCases. +*/ + +import { assert } from 'chai' +import sinon from 'sinon' +import BCHJS from '@psf/bch-js' + +import SlpUseCases from '../../../src/use-cases/slp-use-cases.js' + +describe('#slp-use-cases.js', () => { + let sandbox + let mockAdapters + let mockConfig + let uut + let mockBchjs + let mockWallet + let mockSlpTokenMedia + + beforeEach(() => { + sandbox = sinon.createSandbox() + mockConfig = { + restURL: 'http://localhost:3000/v5/', + ipfsGateway: 'p2wdb-gateway-678.fullstack.cash' + } + + mockAdapters = { + slpIndexer: { + get: sandbox.stub().resolves({}), + post: sandbox.stub().resolves({}) + } + } + + // Create mock BCHJS + mockBchjs = new BCHJS() + mockBchjs.Electrumx = { + txData: sandbox.stub().resolves({ + details: { + vout: [ + { + scriptPubKey: { + hex: '6a0c48656c6c6f20576f726c6421' + } + } + ] + } + }) + } + mockBchjs.Script = { + toASM: sandbox.stub().returns('OP_RETURN 48656c6c6f20576f726c6421') + } + + // Create mock wallet + mockWallet = { + walletInfoPromise: Promise.resolve(), + getTransactions: sandbox.stub().resolves([]), + getTxData: sandbox.stub().resolves([{ + vin: [{ + address: 'bitcoincash:test123' + }] + }]) + } + + // Create mock SlpTokenMedia + mockSlpTokenMedia = { + getIcon: sandbox.stub().resolves({ tokenIcon: 'test-icon.png' }) + } + + // Mock the imports + uut = new SlpUseCases({ + adapters: mockAdapters, + bchjs: mockBchjs, + config: mockConfig + }) + + // Replace the wallet initialization with our mocks + uut.wallet = mockWallet + uut.slpTokenMedia = mockSlpTokenMedia + uut.walletInitialized = true + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('#constructor()', () => { + it('should require adapters', () => { + assert.throws(() => { + // eslint-disable-next-line no-new + new SlpUseCases() + }, /Adapters instance required/) + }) + + it('should require slpIndexer adapter', () => { + assert.throws(() => { + // eslint-disable-next-line no-new + new SlpUseCases({ adapters: {} }) + }, /SLP Indexer adapter required/) + }) + }) + + describe('#getStatus()', () => { + it('should call slpIndexer adapter get method', async () => { + mockAdapters.slpIndexer.get.resolves({ status: 'ok' }) + + const result = await uut.getStatus() + + assert.isTrue(mockAdapters.slpIndexer.get.calledOnceWith('slp/status/')) + assert.deepEqual(result, { status: 'ok' }) + }) + }) + + describe('#getAddress()', () => { + it('should call slpIndexer adapter post method', async () => { + const address = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' + mockAdapters.slpIndexer.post.resolves({ balance: 1000 }) + + const result = await uut.getAddress({ address }) + + assert.isTrue( + mockAdapters.slpIndexer.post.calledOnceWith('slp/address/', { address }) + ) + assert.deepEqual(result, { balance: 1000 }) + }) + }) + + describe('#getTxid()', () => { + it('should call slpIndexer adapter post method', async () => { + const txid = 'a'.repeat(64) + mockAdapters.slpIndexer.post.resolves({ txid }) + + const result = await uut.getTxid({ txid }) + + assert.isTrue( + mockAdapters.slpIndexer.post.calledOnceWith('slp/tx/', { txid }) + ) + assert.deepEqual(result, { txid }) + }) + }) + + describe('#getTokenStats()', () => { + it('should call slpIndexer adapter post method', async () => { + const tokenId = 'a'.repeat(64) + const withTxHistory = false + mockAdapters.slpIndexer.post.resolves({ tokenData: {} }) + + const result = await uut.getTokenStats({ tokenId, withTxHistory }) + + assert.isTrue( + mockAdapters.slpIndexer.post.calledOnceWith('slp/token/', { tokenId, withTxHistory }) + ) + assert.deepEqual(result, { tokenData: {} }) + }) + }) + + describe('#getTokenData()', () => { + it('should get token data with mutable and immutable data', async () => { + const tokenId = 'a'.repeat(64) + const tokenStats = { + tokenData: { + documentUri: 'ipfs://test123', + documentHash: 'b'.repeat(64) + } + } + mockAdapters.slpIndexer.post.resolves(tokenStats) + + // Mock decodeOpReturn to return JSON with mda + sandbox.stub(uut, 'decodeOpReturn').resolves(JSON.stringify({ mda: 'bitcoincash:test123' })) + sandbox.stub(uut, 'getMutableCid').resolves('mutable-cid-123') + + const result = await uut.getTokenData({ tokenId }) + + assert.property(result, 'genesisData') + assert.property(result, 'immutableData') + assert.property(result, 'mutableData') + }) + + it('should handle errors when getting mutable data', async () => { + const tokenId = 'a'.repeat(64) + const tokenStats = { + tokenData: { + documentUri: 'ipfs://test123', + documentHash: 'b'.repeat(64) + } + } + mockAdapters.slpIndexer.post.resolves(tokenStats) + + sandbox.stub(uut, 'getMutableCid').rejects(new Error('Test error')) + + const result = await uut.getTokenData({ tokenId }) + + assert.property(result, 'genesisData') + assert.property(result, 'immutableData') + assert.equal(result.mutableData, '') + }) + }) + + describe('#getTokenData2()', () => { + it('should call slpTokenMedia getIcon method', async () => { + const tokenId = 'a'.repeat(64) + const updateCache = false + mockSlpTokenMedia.getIcon.resolves({ tokenIcon: 'test-icon.png' }) + + const result = await uut.getTokenData2({ tokenId, updateCache }) + + assert.isTrue( + mockSlpTokenMedia.getIcon.calledOnceWith({ tokenId, updateCache }) + ) + assert.deepEqual(result, { tokenIcon: 'test-icon.png' }) + }) + }) + + describe('#decodeOpReturn()', () => { + it('should decode OP_RETURN data from transaction', async () => { + const txid = 'a'.repeat(64) + const mockTxData = { + details: { + vout: [ + { + scriptPubKey: { + hex: '6a0c48656c6c6f20576f726c6421' + } + } + ] + } + } + mockBchjs.Electrumx.txData.resolves(mockTxData) + mockBchjs.Script.toASM.returns('OP_RETURN 48656c6c6f20576f726c6421') + + const result = await uut.decodeOpReturn({ txid }) + + assert.isTrue(mockBchjs.Electrumx.txData.calledOnceWith(txid)) + assert.isString(result) + }) + + it('should throw error if txid is not a string', async () => { + try { + await uut.decodeOpReturn({ txid: null }) + assert.fail('Should have thrown an error') + } catch (err) { + assert.include(err.message, 'txid must be a string') + } + }) + }) + + describe('#getCIDData()', () => { + it('should fetch IPFS data from CID', async () => { + const cid = 'ipfs://test123' + const mockData = { name: 'Test Token' } + + // Mock axios + const axios = await import('axios') + sandbox.stub(axios.default, 'get').resolves({ data: mockData }) + + const result = await uut.getCIDData({ cid }) + + assert.deepEqual(result, mockData) + }) + + it('should throw error if cid is not a string', async () => { + try { + await uut.getCIDData({ cid: null }) + assert.fail('Should have thrown an error') + } catch (err) { + assert.include(err.message, 'cid must be a string') + } + }) + }) + + describe('#getMutableCid()', () => { + it('should extract mutable CID from token stats', async () => { + const tokenStats = { + documentHash: 'a'.repeat(64) + } + + const mockOpReturn = JSON.stringify({ mda: 'bitcoincash:test123' }) + sandbox.stub(uut, 'decodeOpReturn').resolves(mockOpReturn) + + mockWallet.getTransactions.resolves([ + { + tx_hash: 'b'.repeat(64), + height: 100 + } + ]) + + mockWallet.getTxData.resolves([{ + vin: [{ + address: 'bitcoincash:test123' + }] + }]) + + // Mock decodeOpReturn for the transaction + uut.decodeOpReturn.onSecondCall().resolves(JSON.stringify({ cid: 'ipfs://mutable-cid-123', ts: 1234567890 })) + + const result = await uut.getMutableCid({ tokenStats }) + + assert.isString(result) + }) + + it('should return false if no documentHash in tokenStats', async () => { + const tokenStats = {} + + try { + await uut.getMutableCid({ tokenStats }) + assert.fail('Should have thrown an error') + } catch (err) { + assert.include(err.message, 'No documentHash property found') + } + }) + }) +})