diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +dist/ diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index b349f4d..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,29 +0,0 @@ -module.exports = { - env: { - commonjs: true, - es2021: true, - browser: true, - node: true, - 'jest/globals': true, - }, - extends: ['eslint:recommended', 'google'], - plugins: ['jest'], - parserOptions: { - ecmaVersion: 12, - }, - rules: { - 'max-len': [ - 'error', - { - code: 200, - ignoreTrailingComments: true, - ignoreUrls: true, - ignoreStrings: true, - ignoreTemplateLiterals: true, - ignoreRegExpLiterals: true, - }, - ], - 'require-jsdoc': 'off', - 'quote-props': 'off', - }, -}; diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..5c57d75 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,11 @@ +{ + "extends": "./node_modules/gts/", + "overrides": [ + { + "files": ["*.test.ts"], + "rules": { + "@typescript-eslint/no-explicit-any": 0 + } + } + ] +} diff --git a/.gitignore b/.gitignore index 977a377..b4fe34d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ +dist node_modules/ logs/ +.idea/ .vscode/ test.js -package-lock.json .editorconfig +.DS_Store diff --git a/.husky/pre-commit b/.husky/pre-commit index 20d0d06..d1096ab 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -npm run lint +npm run test diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +dist/ diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..ff15483 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('gts/.prettierrc.json') +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f67c340 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,110 @@ +# Changelog + +## [2.0.0] - 2023-10-12 + +### Added + +- **TypeScript support** + + The project was fully rewritten in TypeScript, which means it supports typings now. + + _See [examples](./examples/) directory._ + +- **More API methods** + + Added more API methods: + + ```js + // before + const block = await api.get('blocks/get', { id }); + + // after + const block = await api.getBlock(id); + ``` + + and `post()` method: + + ```js + await api.post('transactions/process', { transaction }); + ``` + +### Fixed + +- **Creating multiple instances** + + Previously, it was not possible to create multiple instances due to the storage of Logger and "Node Manager" data in the modules. + +- **Importing module several times** + + Fixed a bug where importing adamant-api-jsclient caused issues when it was also imported as a dependency. + +### Changed + +- **API Initialization** + + Now you will create new instances of `adamant-api` using keyword `new`: + + ```js + import { AdamantApi } from 'adamant-api'; + + const api = new AdamantApi({ nodes: [/* ... */] }); + ``` + +- **Socket Initialization** + + Replace `api.socket.initSocket()` with `api.initSocket()`. + + Use `api.socket.on()` instead of `.initSocket({ onNewMessage() {} })`. + + ```ts + // before + api.socket.initSocket({ + admAddress: 'U1234..', + onNewMessage(transaction) { + // ... + }, + }); + + // after + api.initSocket({ admAddress: 'U1234..' }); + + api.socket.on((transaction: AnyTransaction) => { + // ... + }); + ``` + + or specify `socket` option when initializing API: + + ```ts + // socket.ts + import { WebSocketClient, TransactionType } from 'adamant-api'; + + const socket = new WebSocketClient({ admAddress: 'U1234..' }); + + socket.on([TransactionType.CHAT_MESSAGE, TransactionType.SEND], (transaction) => { + // handle chat messages and transfer tokens transactions + }); + + socket.on(TransactionType.VOTE, (transaction) => { + // handle vote for delegate transaction + }); + + export { socket }; + ``` + + ```ts + // api.ts + import { AdamantApi } from 'adamant-api'; + import { socket } from './socket'; + + export const api = new AdamantApi({ + socket, + nodes: [/* ... */], + }); + ``` + +### Removeed + +- `createTransaction()` + + Use `createSendTransaction`, `createStateTransaction`, `createChatTransaction`, `createDelegateTransaction`, `createVoteTransaction` methods instead. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f270cba --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,122 @@ +# Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f45a23b..6187678 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,17 +2,23 @@ Before submitting your contribution, please make sure to take a moment and read through the following guidelines: +- [Code of Conduct](./CODE_OF_CONDUCT.md) +- [Issue Reporting Guidelines](#issue-reporting-guidelines) - [Pull Request Guidelines](#pull-request-guidelines) - [Development Setup](#development-setup) - [Scripts](#scripts) - [Project Structure](#project-structure) - [Contributing Tests](#contributing-tests) +## Issue Reporting Guidelines + +- Always use [GitHub Issues](https://github.com/Adamant-im/adamant-api-jsclient/issues) to create new issues. + ## Pull Request Guidelines - The master branch is just a snapshot of the latest stable release. All development should be done in dedicated branches. Do not submit PRs against the master branch. -- Checkout a topic branch from a base branch, e.g. `master`, and merge back against that branch. +- Checkout a topic branch from a base branch, e.g. `dev`, and merge back against that branch. - If adding a new feature add accompanying test case. @@ -20,24 +26,24 @@ Before submitting your contribution, please make sure to take a moment and read - Make sure tests pass! -- Commit messages must follow the [commit message convention](./commit-convention.md). Commit messages are automatically validated before commit (by invoking [Git Hooks](https://git-scm.com/docs/githooks) via [husky](https://github.com/typicode/husky)). +- Commit messages must follow the [commit message convention](https://github.com/angular/angular/blob/68a6a07/CONTRIBUTING.md#commit). Commit messages are automatically validated before commit (by invoking [Git Hooks](https://git-scm.com/docs/githooks) via [husky](https://github.com/typicode/husky)). - No need to worry about code style as long as you have installed the dev dependencies - modified files are automatically formatted with Prettier on commit (by invoking [Git Hooks](https://git-scm.com/docs/githooks) via [husky](https://github.com/typicode/husky)). ## Development Setup -You will need [Node.js](https://nodejs.org) **version 16+**. +You will need [Node.js](https://nodejs.org) **version 18+** and [pnpm](https://pnpm.io/). After cloning the repo, run: ```bash -$ npm i # install the dependencies of the project +$ pnpm i # install the dependencies of the project ``` A high level overview of tools used: -- [Jest](https://jestjs.io/) for unit testing -- [Prettier](https://prettier.io/) for code formatting +- [Jest](https://jestjs.io) for unit testing +- [gts](https://github.com/google/gts) for code formatting ## Scripts @@ -49,7 +55,7 @@ The `lint` script runs linter. # lint files $ npm run lint # fix linter errors -$ npm run lint:fix +$ npm run fix ``` ### `npm run test` @@ -70,22 +76,33 @@ $ npm run test -- fileName $ npm run test -- fileName -t 'test name' ``` +### Generating API types + +To generate API types, run the following commands in the [`adamant-schema`](https://github.com/Adamant-im/adamant-schema/)'s master branch with the latest commit: + +```bash +# build OpenAPI schema +$ npm run bundle +# generate typescript from the schema +$ npx swagger-typescript-api -p ./dist/schema.json -o ./dist -n generated.ts --no-client +``` + +Then, copy `dist/generated.ts` to `adamant-api-jsclient` at `src/api/generated.ts`. + ## Project Structure - **`src`**: contains the source code - **`api`**: contains group of methods and methods for the API. + - **`genearated.ts`**: contains auto-generated types for API. + + - **`coins`**: contains group of utils for coins. + - **`helpers`**: contains utilities shared across the entire codebase. - **`tests`**: contains tests for the helpers directory. - - **`tests`**: contains tests for the src directory. - ## Contributing Tests Unit tests are collocated with the code being tested inside directories named `tests`. Consult the [Jest docs](https://jestjs.io/docs/en/using-matchers) and existing test cases for how to write new test specs. Here are some additional guidelines: - -- Use the minimal API needed for a test case. For example, if a test can be written without involving the reactivity system or a component, it should be written so. This limits the test's exposure to changes in unrelated parts and makes it more stable. - -- Only use platform-specific runtimes if the test is asserting platform-specific behavior. diff --git a/README.md b/README.md index 78caffa..e1451c0 100644 --- a/README.md +++ b/README.md @@ -4,20 +4,20 @@ ADAMANT JavaScript API is a library intended to interact with ADAMANT blockchain Features: -* High reliability -* GET-requests to the blockchain -* Sending tokens -* Sending messages -* Creating a delegate -* Voting for delegates -* Caching public keys -* Encrypting and decrypting of messages -* Forming and signing transactions -* Working with ADM key pairs -* Generating crypto wallets (addresses and keys), bound to ADM account -* Working with ADAMANT epoch time -* Support for WebSocket connections -* Logging warnings, errors, info +- High reliability +- GET-requests to the blockchain +- Sending tokens +- Sending messages +- Creating a delegate +- Voting for delegates +- Caching public keys +- Encrypting and decrypting of messages +- Forming and signing transactions +- Working with ADM key pairs +- Generating crypto wallets (addresses and keys), bound to ADM account +- Working with ADAMANT epoch time +- Support for WebSocket connections +- Logging warnings, errors, info ## Reliability @@ -27,23 +27,18 @@ Health Check system pings all nodes in the list using [`/status`](https://github ## Usage -Add current version of ADAMANT JavaScript API library in project's `package.json` in `dependencies` section: +Install library from npm: -``` json - "dependencies": { - "adamant-api": "^1.4.0", -``` - -Or install library from npm: - -``` bash +```bash npm i adamant-api ``` Initialize the library: -``` JS -const nodesList = [ +```js +const { AdamantApi } = require('adamant-api') + +const nodes = [ "http://localhost:36666", "https://endless.adamant.im", "https://clown.adamant.im", @@ -51,15 +46,18 @@ const nodesList = [ "http://88.198.156.44:36666", "https://lake.adamant.im" ]; -const api = require('adamant-api')({ node: nodesList, logLevel: 'info' }); + +const api = new AdamantApi({ + nodes, +}); ``` Request example: -``` JS -api.get('blocks').then(response => { - console.log(response.data) -}) +```js +const response = await api.getBlocks() + +console.log(response.data) ``` ## Documentation diff --git a/commitlint.config.js b/commitlint.config.js index c34aa79..84dcb12 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,3 +1,3 @@ module.exports = { - extends: ['@commitlint/config-conventional'] + extends: ['@commitlint/config-conventional'], }; diff --git a/examples/basic/index.ts b/examples/basic/index.ts new file mode 100644 index 0000000..ba49ff1 --- /dev/null +++ b/examples/basic/index.ts @@ -0,0 +1,35 @@ +import {AdamantApi} from 'adamant-api'; + +const nodes = [ + 'https://endless.adamant.im', + 'https://clown.adamant.im', + 'http://23.226.231.225:36666', + 'http://88.198.156.44:36666', + 'https://lake.adamant.im', +]; + +const api = new AdamantApi({ + nodes, +}); + +/** + * Make sure we are using active node with least ping + */ +api.onReady(async () => { + /** + * Use API bind to make request, alternative you can use: + * ```js + * const response = await api.get('peers/version'); + * ``` + */ + const response = await api.getNodeVersion(); + + if (!response.success) { + console.log(`Something bad happened: ${response.errorMessage}`); + return; + } + + const {version} = response; + + console.log(`Connected node is using ADAMANT v${version}`); +}); diff --git a/examples/getId/index.ts b/examples/getId/index.ts new file mode 100644 index 0000000..6374bdb --- /dev/null +++ b/examples/getId/index.ts @@ -0,0 +1,23 @@ +import {getTransactionId} from 'adamant-api'; + +const id = getTransactionId({ + type: 8, + amount: 0, + timestamp: 194049840, + asset: { + chat: { + message: '7189aba904138dd1d53948ed1e5b1d18a11ba1910834', + own_message: '8b717d0a9142e697cafd342c8f79f042c47a9e712e8a61b6', + type: 1, + }, + }, + recipientId: 'U12605277787100066317', + senderId: 'U8084717991279447871', + senderPublicKey: + '09c93f2667728c62d2279bbb8df34c3856088290167f557c33594dc212da054a', + signature: + '304a4cb7e11651d576e2c4dffb4100bef5385981807f18c3267c863daf60bd277706e6790157beacf5100c77b6798c4725f2f4e070ca78496ff53a4c2e437f02', +}); + +// Transaction id: 5505818610983968576 +console.log(`Transaction id: ${id}`); diff --git a/examples/socket/index.ts b/examples/socket/index.ts new file mode 100644 index 0000000..fcedf4c --- /dev/null +++ b/examples/socket/index.ts @@ -0,0 +1,69 @@ +import { + AdamantApi, + TransactionType, + decodeMessage, + WebSocketClient, + ChatMessageTransaction, +} from 'adamant-api'; + +const nodes = [ + 'https://endless.adamant.im', + 'https://clown.adamant.im', + 'http://23.226.231.225:36666', + 'http://88.198.156.44:36666', + 'https://lake.adamant.im', +]; + +/** + * ADM address to subscribe to notifications + */ +const admAddress = process.env.ADAMANT_ADDRESS as `U${string}`; +/** + * Pass phrase to decode messages + */ +const passphrase = process.env.PASSPHRASE!; + +const socket = new WebSocketClient({admAddress}); + +function onChatTransaction(transaction: ChatMessageTransaction) { + const {chat} = transaction.asset; + + const decoded = decodeMessage( + chat.message, + transaction.senderPublicKey, + passphrase, + chat.own_message + ); + + console.log( + `Got a new message from ${transaction.senderId}:\n "${decoded}"` + ); +} + +/** + * Handle chat messages only + */ +socket.on(TransactionType.CHAT_MESSAGE, onChatTransaction); + +// or + +// socket.on((transaction: AnyTransaction) => { +// if (transaction.type === TransactionType.CHAT_MESSAGE) { +// console.log(transaction); +// } +// }); + +setTimeout(() => { + console.log('the handler has been removed'); + /** + * Remove the handler from all types + */ + socket.off(onChatTransaction); +}, 60 * 1000); + +const api = new AdamantApi({ + nodes, + socket, +}); + +export default api; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..3745fc2 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/keys.js b/keys.js deleted file mode 100644 index ba75bd9..0000000 --- a/keys.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./src/helpers/keys') diff --git a/package.json b/package.json index 14f67a7..7cb7a7f 100644 --- a/package.json +++ b/package.json @@ -1,76 +1,95 @@ { - "name": "adamant-api", - "version": "1.8.0", - "description": "REST API for ADAMANT Blockchain", - "main": "src/index.js", - "scripts": { - "test": "jest", - "lint": "eslint src", - "lint:fix": "eslint --fix src", - "prepare": "husky install" - }, - "author": "ADAMANT Foundation (https://adamant.im)", - "license": "GPL-3.0", - "dependencies": { - "@liskhq/lisk-cryptography": "3.2.1", - "axios": "^1.2.1", - "bignumber.js": "^9.1.1", - "bitcoinjs-lib": "^6.1.0", - "bitcore-mnemonic": "^8.25.40", - "bytebuffer": "^5.0.1", - "coininfo": "^5.2.1", - "ecpair": "^2.1.0", - "ed2curve": "^0.3.0", - "ethereumjs-util": "^7.1.5", - "hdkey": "^2.0.1", - "pbkdf2": "^3.1.2", - "socket.io-client": "^4.5.4", - "sodium-browserify-tweetnacl": "^0.2.6", - "tiny-secp256k1": "^2.2.1" - }, - "publishConfig": { - "access": "public" - }, - "keywords": [ - "adm", - "adamant", - "blockchain", - "messenger", - "chat", - "decentralized messenger", - "anonymous messenger", - "secure messenger", - "wallet", - "crypto wallet", - "private keys", - "communication", - "decentralized", - "decentralization", - "anonymous", - "anonymity", - "secure", - "encrypted", - "encryption", - "crypto", - "cryptocurrency", - "bitcoin", - "ethereum" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/Adamant-im/adamant-api-jsclient.git" - }, - "bugs": { - "url": "https://github.com/Adamant-im/adamant-api-jsclient/issues" - }, - "homepage": "https://github.com/Adamant-im/adamant-api-jsclient#readme", - "devDependencies": { - "@commitlint/cli": "^17.3.0", - "@commitlint/config-conventional": "^17.3.0", - "eslint": "^8.30.0", - "eslint-config-google": "^0.14.0", - "eslint-plugin-jest": "^27.1.7", - "husky": "^8.0.2", - "jest": "^29.3.1" - } + "name": "adamant-api", + "version": "2.0.0", + "description": "JavaScript API library for the ADAMANT Blockchain", + "keywords": [ + "adm", + "adamant", + "blockchain", + "messenger", + "chat", + "decentralized messenger", + "anonymous messenger", + "secure messenger", + "wallet", + "crypto wallet", + "private keys", + "communication", + "decentralized", + "decentralization", + "anonymous", + "anonymity", + "secure", + "encrypted", + "encryption", + "crypto", + "cryptocurrency", + "bitcoin", + "ethereum" + ], + "homepage": "https://github.com/Adamant-im/adamant-api-jsclient#readme", + "bugs": { + "url": "https://github.com/Adamant-im/adamant-api-jsclient/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Adamant-im/adamant-api-jsclient.git" + }, + "license": "GPL-3.0", + "author": "ADAMANT Foundation (https://adamant.im)", + "main": "dist/index.js", + "src": "src/index.ts", + "types": "dist/index.d.ts", + "files": ["dist/"], + "scripts": { + "test": "jest", + "lint": "gts lint", + "preinstall": "npx only-allow pnpm", + "prepare": "husky install", + "clean": "gts clean", + "compile": "tsc", + "fix": "gts fix", + "pretest": "npm run compile", + "posttest": "npm run lint" + }, + "dependencies": { + "@liskhq/lisk-cryptography": "3.3.0", + "axios": "^1.5.1", + "bignumber.js": "^9.1.2", + "bip39": "^3.1.0", + "bitcoinjs-lib": "^6.1.5", + "bytebuffer": "^5.0.1", + "coininfo": "^5.2.1", + "ecpair": "^2.1.0", + "ed2curve": "^0.3.0", + "ethereumjs-util": "^7.1.5", + "hdkey": "^2.1.0", + "pbkdf2": "^3.1.2", + "socket.io-client": "^4.7.2", + "sodium-browserify-tweetnacl": "^0.2.6", + "tiny-secp256k1": "^2.2.3", + "tweetnacl": "^1.0.3" + }, + "devDependencies": { + "@commitlint/cli": "^17.7.2", + "@commitlint/config-conventional": "^17.7.0", + "@types/bytebuffer": "^5.0.45", + "@types/hdkey": "^2.0.1", + "@types/ed2curve": "^0.2.2", + "@types/jest": "^29.5.5", + "@types/node": "20.8.5", + "@types/pbkdf2": "^3.1.0", + "eslint": "^8.51.0", + "eslint-config-google": "^0.14.0", + "eslint-plugin-jest": "^27.4.2", + "husky": "^8.0.3", + "jest": "^29.7.0", + "prettier": "^3.0.3", + "ts-jest": "^29.1.1", + "gts": "^5.2.0", + "typescript": "~5.1.6" + }, + "publishConfig": { + "access": "public" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..f710bad --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4933 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@liskhq/lisk-cryptography': + specifier: 3.3.0 + version: 3.3.0 + axios: + specifier: ^1.5.1 + version: 1.5.1 + bignumber.js: + specifier: ^9.1.2 + version: 9.1.2 + bip39: + specifier: ^3.1.0 + version: 3.1.0 + bitcoinjs-lib: + specifier: ^6.1.5 + version: 6.1.5 + bytebuffer: + specifier: ^5.0.1 + version: 5.0.1 + coininfo: + specifier: ^5.2.1 + version: 5.2.1 + ecpair: + specifier: ^2.1.0 + version: 2.1.0 + ed2curve: + specifier: ^0.3.0 + version: 0.3.0 + ethereumjs-util: + specifier: ^7.1.5 + version: 7.1.5 + hdkey: + specifier: ^2.1.0 + version: 2.1.0 + pbkdf2: + specifier: ^3.1.2 + version: 3.1.2 + socket.io-client: + specifier: ^4.7.2 + version: 4.7.2 + sodium-browserify-tweetnacl: + specifier: ^0.2.6 + version: 0.2.6 + tiny-secp256k1: + specifier: ^2.2.3 + version: 2.2.3 + tweetnacl: + specifier: ^1.0.3 + version: 1.0.3 + +devDependencies: + '@commitlint/cli': + specifier: ^17.7.2 + version: 17.7.2 + '@commitlint/config-conventional': + specifier: ^17.7.0 + version: 17.7.0 + '@types/bytebuffer': + specifier: ^5.0.45 + version: 5.0.45 + '@types/ed2curve': + specifier: ^0.2.2 + version: 0.2.2 + '@types/hdkey': + specifier: ^2.0.1 + version: 2.0.1 + '@types/jest': + specifier: ^29.5.5 + version: 29.5.5 + '@types/node': + specifier: 20.8.5 + version: 20.8.5 + '@types/pbkdf2': + specifier: ^3.1.0 + version: 3.1.0 + eslint: + specifier: ^8.51.0 + version: 8.51.0 + eslint-config-google: + specifier: ^0.14.0 + version: 0.14.0(eslint@8.51.0) + eslint-plugin-jest: + specifier: ^27.4.2 + version: 27.4.2(eslint@8.51.0)(jest@29.7.0)(typescript@5.1.6) + gts: + specifier: ^5.2.0 + version: 5.2.0(typescript@5.1.6) + husky: + specifier: ^8.0.3 + version: 8.0.3 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@20.8.5)(ts-node@10.9.1) + prettier: + specifier: ^3.0.3 + version: 3.0.3 + ts-jest: + specifier: ^29.1.1 + version: 29.1.1(@babel/core@7.23.0)(jest@29.7.0)(typescript@5.1.6) + typescript: + specifier: ~5.1.6 + version: 5.1.6 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + dev: true + + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.20 + chalk: 2.4.2 + dev: true + + /@babel/compat-data@7.22.20: + resolution: {integrity: sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.23.0: + resolution: {integrity: sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.0 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) + '@babel/helpers': 7.23.1 + '@babel/parser': 7.23.0 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.0 + '@babel/types': 7.23.0 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.23.0: + resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + jsesc: 2.5.2 + dev: true + + /@babel/helper-compilation-targets@7.22.15: + resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.22.20 + '@babel/helper-validator-option': 7.22.15 + browserslist: 4.22.1 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.0): + resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.22.15: + resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.23.1: + resolution: {integrity: sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.0 + '@babel/types': 7.23.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser@7.23.0: + resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.0): + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.0): + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.0): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.0): + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.0): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.0): + resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.0): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.0): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.0): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.0): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.0): + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.0): + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.0): + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.23.0): + resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + dev: true + + /@babel/traverse@7.23.0: + resolution: {integrity: sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.23.0: + resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + /@commitlint/cli@17.7.2: + resolution: {integrity: sha512-t3N7TZq7lOeqTOyEgfGcaltHqEJf7YDlPg75MldeVPPyz14jZq/+mbGF9tueDLFX8R6RwdymrN6D+U5XwZ8Iwg==} + engines: {node: '>=v14'} + hasBin: true + dependencies: + '@commitlint/format': 17.4.4 + '@commitlint/lint': 17.7.0 + '@commitlint/load': 17.7.2 + '@commitlint/read': 17.5.1 + '@commitlint/types': 17.4.4 + execa: 5.1.1 + lodash.isfunction: 3.0.9 + resolve-from: 5.0.0 + resolve-global: 1.0.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + dev: true + + /@commitlint/config-conventional@17.7.0: + resolution: {integrity: sha512-iicqh2o6et+9kWaqsQiEYZzfLbtoWv9uZl8kbI8EGfnc0HeGafQBF7AJ0ylN9D/2kj6txltsdyQs8+2fTMwWEw==} + engines: {node: '>=v14'} + dependencies: + conventional-changelog-conventionalcommits: 6.1.0 + dev: true + + /@commitlint/config-validator@17.6.7: + resolution: {integrity: sha512-vJSncmnzwMvpr3lIcm0I8YVVDJTzyjy7NZAeXbTXy+MPUdAr9pKyyg7Tx/ebOQ9kqzE6O9WT6jg2164br5UdsQ==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/types': 17.4.4 + ajv: 8.12.0 + dev: true + + /@commitlint/ensure@17.6.7: + resolution: {integrity: sha512-mfDJOd1/O/eIb/h4qwXzUxkmskXDL9vNPnZ4AKYKiZALz4vHzwMxBSYtyL2mUIDeU9DRSpEUins8SeKtFkYHSw==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/types': 17.4.4 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.upperfirst: 4.3.1 + dev: true + + /@commitlint/execute-rule@17.4.0: + resolution: {integrity: sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==} + engines: {node: '>=v14'} + dev: true + + /@commitlint/format@17.4.4: + resolution: {integrity: sha512-+IS7vpC4Gd/x+uyQPTAt3hXs5NxnkqAZ3aqrHd5Bx/R9skyCAWusNlNbw3InDbAK6j166D9asQM8fnmYIa+CXQ==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/types': 17.4.4 + chalk: 4.1.2 + dev: true + + /@commitlint/is-ignored@17.7.0: + resolution: {integrity: sha512-043rA7m45tyEfW7Zv2vZHF++176MLHH9h70fnPoYlB1slKBeKl8BwNIlnPg4xBdRBVNPaCqvXxWswx2GR4c9Hw==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/types': 17.4.4 + semver: 7.5.4 + dev: true + + /@commitlint/lint@17.7.0: + resolution: {integrity: sha512-TCQihm7/uszA5z1Ux1vw+Nf3yHTgicus/+9HiUQk+kRSQawByxZNESeQoX9ujfVd3r4Sa+3fn0JQAguG4xvvbA==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/is-ignored': 17.7.0 + '@commitlint/parse': 17.7.0 + '@commitlint/rules': 17.7.0 + '@commitlint/types': 17.4.4 + dev: true + + /@commitlint/load@17.7.2: + resolution: {integrity: sha512-XA7WTnsjHZ4YH6ZYsrnxgLdXzriwMMq+utZUET6spbOEEIPBCDLdOQXS26P+v3TTO4hUHOEhzUquaBv3jbBixw==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/config-validator': 17.6.7 + '@commitlint/execute-rule': 17.4.0 + '@commitlint/resolve-extends': 17.6.7 + '@commitlint/types': 17.4.4 + '@types/node': 20.5.1 + chalk: 4.1.2 + cosmiconfig: 8.3.6(typescript@5.1.6) + cosmiconfig-typescript-loader: 4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6)(ts-node@10.9.1)(typescript@5.1.6) + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + resolve-from: 5.0.0 + ts-node: 10.9.1(@types/node@20.8.5)(typescript@5.1.6) + typescript: 5.1.6 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + dev: true + + /@commitlint/message@17.4.2: + resolution: {integrity: sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q==} + engines: {node: '>=v14'} + dev: true + + /@commitlint/parse@17.7.0: + resolution: {integrity: sha512-dIvFNUMCUHqq5Abv80mIEjLVfw8QNuA4DS7OWip4pcK/3h5wggmjVnlwGCDvDChkw2TjK1K6O+tAEV78oxjxag==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/types': 17.4.4 + conventional-changelog-angular: 6.0.0 + conventional-commits-parser: 4.0.0 + dev: true + + /@commitlint/read@17.5.1: + resolution: {integrity: sha512-7IhfvEvB//p9aYW09YVclHbdf1u7g7QhxeYW9ZHSO8Huzp8Rz7m05aCO1mFG7G8M+7yfFnXB5xOmG18brqQIBg==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/top-level': 17.4.0 + '@commitlint/types': 17.4.4 + fs-extra: 11.1.1 + git-raw-commits: 2.0.11 + minimist: 1.2.8 + dev: true + + /@commitlint/resolve-extends@17.6.7: + resolution: {integrity: sha512-PfeoAwLHtbOaC9bGn/FADN156CqkFz6ZKiVDMjuC2N5N0740Ke56rKU7Wxdwya8R8xzLK9vZzHgNbuGhaOVKIg==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/config-validator': 17.6.7 + '@commitlint/types': 17.4.4 + import-fresh: 3.3.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + resolve-global: 1.0.0 + dev: true + + /@commitlint/rules@17.7.0: + resolution: {integrity: sha512-J3qTh0+ilUE5folSaoK91ByOb8XeQjiGcdIdiB/8UT1/Rd1itKo0ju/eQVGyFzgTMYt8HrDJnGTmNWwcMR1rmA==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/ensure': 17.6.7 + '@commitlint/message': 17.4.2 + '@commitlint/to-lines': 17.4.0 + '@commitlint/types': 17.4.4 + execa: 5.1.1 + dev: true + + /@commitlint/to-lines@17.4.0: + resolution: {integrity: sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg==} + engines: {node: '>=v14'} + dev: true + + /@commitlint/top-level@17.4.0: + resolution: {integrity: sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g==} + engines: {node: '>=v14'} + dependencies: + find-up: 5.0.0 + dev: true + + /@commitlint/types@17.4.4: + resolution: {integrity: sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ==} + engines: {node: '>=v14'} + dependencies: + chalk: 4.1.2 + dev: true + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.50.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.51.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.51.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.9.1: + resolution: {integrity: sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.2: + resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.22.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.50.0: + resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@eslint/js@8.51.0: + resolution: {integrity: sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.11: + resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@istanbuljs/load-nyc-config@1.1.0: + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + dev: true + + /@istanbuljs/schema@0.1.3: + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + dev: true + + /@jest/console@29.7.0: + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.8.5 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/core@29.7.0(ts-node@10.9.1): + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.8.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.8.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.8.5)(ts-node@10.9.1) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /@jest/environment@29.7.0: + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.8.5 + jest-mock: 29.7.0 + dev: true + + /@jest/expect-utils@29.7.0: + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + dev: true + + /@jest/expect@29.7.0: + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/fake-timers@29.7.0: + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 20.8.5 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + dev: true + + /@jest/globals@29.7.0: + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/reporters@29.7.0: + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.19 + '@types/node': 20.8.5 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-instrument: 6.0.0 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.6 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jest/source-map@29.6.3: + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.19 + callsites: 3.1.0 + graceful-fs: 4.2.11 + dev: true + + /@jest/test-result@29.7.0: + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.4 + collect-v8-coverage: 1.0.2 + dev: true + + /@jest/test-sequencer@29.7.0: + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/transform@29.7.0: + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.23.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.19 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.5 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.2 + '@types/node': 20.8.5 + '@types/yargs': 17.0.26 + chalk: 4.1.2 + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.19 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.19: + resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@liskhq/lisk-cryptography@3.3.0: + resolution: {integrity: sha512-nVd1dWjsL1rLNeVt1qCROpflaaZx6o93Aa+FXhnUTTyNqYrWy5OjCU9eqDYUsfqA3GnYCaIbuG9crmKWf6XHVQ==} + engines: {node: '>=18.12.0 <=18', npm: '>=8.1.0'} + dependencies: + buffer-reverse: 1.0.1 + ed2curve: 0.3.0 + tweetnacl: 1.0.3 + varuint-bitcoin: 1.1.2 + optionalDependencies: + sodium-native: 3.2.1 + dev: false + + /@noble/hashes@1.3.2: + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + dev: false + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@pkgr/utils@2.4.2: + resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + dependencies: + cross-spawn: 7.0.3 + fast-glob: 3.3.1 + is-glob: 4.0.3 + open: 9.1.0 + picocolors: 1.0.0 + tslib: 2.6.2 + dev: true + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@sinonjs/commons@3.0.0: + resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} + dependencies: + type-detect: 4.0.8 + dev: true + + /@sinonjs/fake-timers@10.3.0: + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + dependencies: + '@sinonjs/commons': 3.0.0 + dev: true + + /@socket.io/component-emitter@3.1.0: + resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} + dev: false + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + + /@types/babel__core@7.20.2: + resolution: {integrity: sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==} + dependencies: + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + '@types/babel__generator': 7.6.5 + '@types/babel__template': 7.4.2 + '@types/babel__traverse': 7.20.2 + dev: true + + /@types/babel__generator@7.6.5: + resolution: {integrity: sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@types/babel__template@7.4.2: + resolution: {integrity: sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==} + dependencies: + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + dev: true + + /@types/babel__traverse@7.20.2: + resolution: {integrity: sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@types/bn.js@5.1.2: + resolution: {integrity: sha512-dkpZu0szUtn9UXTmw+e0AJFd4D2XAxDnsCLdc05SfqpqzPEBft8eQr8uaFitfo/dUUOZERaLec2hHMG87A4Dxg==} + dependencies: + '@types/node': 20.8.5 + dev: false + + /@types/bytebuffer@5.0.45: + resolution: {integrity: sha512-eqC6fZ9ZC3R37gKE5vH3S+SnMEVvGoaopn8sNwP2Xk1FDt6mn8hYxqFPgBS389SDBfxb6+j5Ss6VvffZI6YW1A==} + dependencies: + '@types/long': 3.0.32 + '@types/node': 20.8.5 + dev: true + + /@types/ed2curve@0.2.2: + resolution: {integrity: sha512-G1sTX5xo91ydevQPINbL2nfgVAj/s1ZiqZxC8OCWduwu+edoNGUm5JXtTkg9F3LsBZbRI46/0HES4CPUE2wc9g==} + dependencies: + tweetnacl: 1.0.3 + dev: true + + /@types/graceful-fs@4.1.7: + resolution: {integrity: sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==} + dependencies: + '@types/node': 20.8.5 + dev: true + + /@types/hdkey@2.0.1: + resolution: {integrity: sha512-4s6jhP0BT3sWBaRo0BfA3BpFllQvuRlXUzIqnuvSNmPhmMCZYjhrDe31nJb8kJLc0ZVdqj9Ep9eH2RGBNCK9Sw==} + dependencies: + '@types/node': 20.8.5 + dev: true + + /@types/istanbul-lib-coverage@2.0.4: + resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + dev: true + + /@types/istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==} + dependencies: + '@types/istanbul-lib-coverage': 2.0.4 + dev: true + + /@types/istanbul-reports@3.0.2: + resolution: {integrity: sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==} + dependencies: + '@types/istanbul-lib-report': 3.0.1 + dev: true + + /@types/jest@29.5.5: + resolution: {integrity: sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==} + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /@types/json-schema@7.0.13: + resolution: {integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==} + dev: true + + /@types/long@3.0.32: + resolution: {integrity: sha512-ZXyOOm83p7X8p3s0IYM3VeueNmHpkk/yMlP8CLeOnEcu6hIwPH7YjZBvhQkR0ZFS2DqZAxKtJ/M5fcuv3OU5BA==} + dev: true + + /@types/minimist@1.2.3: + resolution: {integrity: sha512-ZYFzrvyWUNhaPomn80dsMNgMeXxNWZBdkuG/hWlUvXvbdUH8ZERNBGXnU87McuGcWDsyzX2aChCv/SVN348k3A==} + dev: true + + /@types/node@20.5.1: + resolution: {integrity: sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==} + dev: true + + /@types/node@20.8.5: + resolution: {integrity: sha512-SPlobFgbidfIeOYlzXiEjSYeIJiOCthv+9tSQVpvk4PAdIIc+2SmjNVzWXk9t0Y7dl73Zdf+OgXKHX9XtkqUpw==} + dependencies: + undici-types: 5.25.3 + + /@types/normalize-package-data@2.4.2: + resolution: {integrity: sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==} + dev: true + + /@types/pbkdf2@3.1.0: + resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==} + dependencies: + '@types/node': 20.8.5 + + /@types/secp256k1@4.0.4: + resolution: {integrity: sha512-oN0PFsYxDZnX/qSJ5S5OwaEDTYfekhvaM5vqui2bu1AA39pKofmgL104Q29KiOXizXS2yLjSzc5YdTyMKdcy4A==} + dependencies: + '@types/node': 20.8.5 + dev: false + + /@types/semver@7.5.3: + resolution: {integrity: sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==} + dev: true + + /@types/stack-utils@2.0.1: + resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + dev: true + + /@types/yargs-parser@21.0.1: + resolution: {integrity: sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==} + dev: true + + /@types/yargs@17.0.26: + resolution: {integrity: sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==} + dependencies: + '@types/yargs-parser': 21.0.1 + dev: true + + /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.50.0)(typescript@5.1.6): + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.9.1 + '@typescript-eslint/parser': 5.62.0(eslint@8.51.0)(typescript@5.1.6) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.50.0)(typescript@5.1.6) + '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.1.6) + debug: 4.3.4 + eslint: 8.50.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + semver: 7.5.4 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@5.62.0(eslint@8.51.0)(typescript@5.1.6): + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) + debug: 4.3.4 + eslint: 8.51.0 + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@5.62.0: + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + dev: true + + /@typescript-eslint/type-utils@5.62.0(eslint@8.50.0)(typescript@5.1.6): + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) + '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.1.6) + debug: 4.3.4 + eslint: 8.50.0 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@5.62.0: + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree@5.62.0(typescript@5.1.6): + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@5.62.0(eslint@8.50.0)(typescript@5.1.6): + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@types/json-schema': 7.0.13 + '@types/semver': 7.5.3 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) + eslint: 8.50.0 + eslint-scope: 5.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/utils@5.62.0(eslint@8.51.0)(typescript@5.1.6): + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) + '@types/json-schema': 7.0.13 + '@types/semver': 7.5.3 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) + eslint: 8.51.0 + eslint-scope: 5.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@5.62.0: + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + dev: true + + /acorn-jsx@5.3.2(acorn@8.10.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.10.0 + dev: true + + /acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + dev: true + + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + + /axios@1.5.1: + resolution: {integrity: sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==} + dependencies: + follow-redirects: 1.15.3 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + + /babel-jest@29.7.0(@babel/core@7.23.0): + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.23.0 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.2 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.23.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + dependencies: + '@babel/helper-plugin-utils': 7.22.5 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 + '@types/babel__core': 7.20.2 + '@types/babel__traverse': 7.20.2 + dev: true + + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.0): + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.0) + dev: true + + /babel-preset-jest@29.6.3(@babel/core@7.23.0): + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.0 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.0) + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /base-x@3.0.9: + resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /base-x@4.0.0: + resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==} + dev: false + + /bech32@2.0.0: + resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==} + dev: false + + /big-integer@1.6.51: + resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} + engines: {node: '>=0.6'} + dev: true + + /bignumber.js@9.1.2: + resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} + dev: false + + /bip174@2.1.1: + resolution: {integrity: sha512-mdFV5+/v0XyNYXjBS6CQPLo9ekCx4gtKZFnJm5PMto7Fs9hTTDpkkzOB7/FtluRI6JbUUAu+snTYfJRgHLZbZQ==} + engines: {node: '>=8.0.0'} + dev: false + + /bip39@3.1.0: + resolution: {integrity: sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==} + dependencies: + '@noble/hashes': 1.3.2 + dev: false + + /bitcoinjs-lib@6.1.5: + resolution: {integrity: sha512-yuf6xs9QX/E8LWE2aMJPNd0IxGofwfuVOiYdNUESkc+2bHHVKjhJd8qewqapeoolh9fihzHGoDCB5Vkr57RZCQ==} + engines: {node: '>=8.0.0'} + dependencies: + '@noble/hashes': 1.3.2 + bech32: 2.0.0 + bip174: 2.1.1 + bs58check: 3.0.1 + typeforce: 1.18.0 + varuint-bitcoin: 1.1.2 + dev: false + + /blakejs@1.2.1: + resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} + dev: false + + /bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + dev: false + + /bn.js@5.2.1: + resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + dev: false + + /bplist-parser@0.2.0: + resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} + engines: {node: '>= 5.10.0'} + dependencies: + big-integer: 1.6.51 + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + dev: false + + /browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.4 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + + /browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001542 + electron-to-chromium: 1.4.537 + node-releases: 2.0.13 + update-browserslist-db: 1.0.13(browserslist@4.22.1) + dev: true + + /bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + dependencies: + fast-json-stable-stringify: 2.1.0 + dev: true + + /bs58@4.0.1: + resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + dependencies: + base-x: 3.0.9 + dev: false + + /bs58@5.0.0: + resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + dependencies: + base-x: 4.0.0 + dev: false + + /bs58check@2.1.2: + resolution: {integrity: sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==} + dependencies: + bs58: 4.0.1 + create-hash: 1.2.0 + safe-buffer: 5.2.1 + dev: false + + /bs58check@3.0.1: + resolution: {integrity: sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==} + dependencies: + '@noble/hashes': 1.3.2 + bs58: 5.0.0 + dev: false + + /bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + dependencies: + node-int64: 0.4.0 + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /buffer-reverse@1.0.1: + resolution: {integrity: sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg==} + dev: false + + /buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + dev: false + + /bundle-name@3.0.0: + resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} + engines: {node: '>=12'} + dependencies: + run-applescript: 5.0.0 + dev: true + + /bytebuffer@5.0.1: + resolution: {integrity: sha512-IuzSdmADppkZ6DlpycMkm8l9zeEq16fWtLvunEwFiYciR/BHo4E8/xs5piFquG+Za8OWmMqHF8zuRviz2LHvRQ==} + engines: {node: '>=0.8'} + dependencies: + long: 3.2.0 + dev: false + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase-keys@6.2.2: + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + map-obj: 4.3.0 + quick-lru: 4.0.1 + dev: true + + /camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /caniuse-lite@1.0.30001542: + resolution: {integrity: sha512-UrtAXVcj1mvPBFQ4sKd38daP8dEcXXr5sQe6QNNinaPd0iA/cxg9/l3VrSdL73jgw5sKyuQ6jNgiKO12W3SsVA==} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + dev: true + + /chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true + + /chloride-test@1.2.4: + resolution: {integrity: sha512-9vhoi1qXSBPn6//ZxIgSe3M2QhKHzIPZQzmrZgmPADsqW0Jxpe3db1e7aGSRUMXbxAQ04SfypdT8dGaSvIvKDw==} + dependencies: + json-buffer: 2.0.11 + dev: false + + /ci-info@3.8.0: + resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} + engines: {node: '>=8'} + dev: true + + /cipher-base@1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + + /cjs-module-lexer@1.2.3: + resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + dev: true + + /cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + + /cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + dev: true + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true + + /coininfo@5.2.1: + resolution: {integrity: sha512-mqSQIhAMgeiySzS0Ei33qno0oN/JQt9X3I6J1zx4eIYUnObGPuoLOrpssoqU84ZQoIpJGt3mQdEd2dL3sZJADw==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + + /compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /conventional-changelog-angular@6.0.0: + resolution: {integrity: sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==} + engines: {node: '>=14'} + dependencies: + compare-func: 2.0.0 + dev: true + + /conventional-changelog-conventionalcommits@6.1.0: + resolution: {integrity: sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==} + engines: {node: '>=14'} + dependencies: + compare-func: 2.0.0 + dev: true + + /conventional-commits-parser@4.0.0: + resolution: {integrity: sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==} + engines: {node: '>=14'} + hasBin: true + dependencies: + JSONStream: 1.3.5 + is-text-path: 1.0.1 + meow: 8.1.2 + split2: 3.2.2 + dev: true + + /convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: true + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /cosmiconfig-typescript-loader@4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6)(ts-node@10.9.1)(typescript@5.1.6): + resolution: {integrity: sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw==} + engines: {node: '>=v14.21.3'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=7' + ts-node: '>=10' + typescript: '>=4' + dependencies: + '@types/node': 20.5.1 + cosmiconfig: 8.3.6(typescript@5.1.6) + ts-node: 10.9.1(@types/node@20.8.5)(typescript@5.1.6) + typescript: 5.1.6 + dev: true + + /cosmiconfig@8.3.6(typescript@5.1.6): + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + typescript: 5.1.6 + dev: true + + /create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + dependencies: + cipher-base: 1.0.4 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.2 + sha.js: 2.4.11 + dev: false + + /create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + dependencies: + cipher-base: 1.0.4 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + dev: false + + /create-jest@29.7.0(@types/node@20.8.5)(ts-node@10.9.1): + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.8.5)(ts-node@10.9.1) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /dargs@7.0.0: + resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} + engines: {node: '>=8'} + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + /decamelize-keys@1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + dev: true + + /decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + dev: true + + /dedent@1.5.1: + resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + + /default-browser-id@3.0.0: + resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} + engines: {node: '>=12'} + dependencies: + bplist-parser: 0.2.0 + untildify: 4.0.0 + dev: true + + /default-browser@4.0.0: + resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} + engines: {node: '>=14.16'} + dependencies: + bundle-name: 3.0.0 + default-browser-id: 3.0.0 + execa: 7.2.0 + titleize: 3.0.0 + dev: true + + /define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + dev: true + + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + + /detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + dev: true + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + dependencies: + is-obj: 2.0.0 + dev: true + + /ecpair@2.1.0: + resolution: {integrity: sha512-cL/mh3MtJutFOvFc27GPZE2pWL3a3k4YvzUWEOvilnfZVlH3Jwgx/7d6tlD7/75tNk8TG2m+7Kgtz0SI1tWcqw==} + engines: {node: '>=8.0.0'} + dependencies: + randombytes: 2.1.0 + typeforce: 1.18.0 + wif: 2.0.6 + dev: false + + /ed2curve@0.1.4: + resolution: {integrity: sha512-hDZWhCHZ1wu4P2g2RVsM2MjDmmJzhvcsXr5qHUSBJZXvuhJSunhbVsWoBXdIe0/yTa3RV4UaWpOmFmrVsKr0wA==} + dependencies: + tweetnacl: 0.14.5 + dev: false + + /ed2curve@0.3.0: + resolution: {integrity: sha512-8w2fmmq3hv9rCrcI7g9hms2pMunQr1JINfcjwR9tAyZqhtyaMN991lF/ZfHfr5tzZQ8c7y7aBgZbjfbd0fjFwQ==} + dependencies: + tweetnacl: 1.0.3 + dev: false + + /electron-to-chromium@1.4.537: + resolution: {integrity: sha512-W1+g9qs9hviII0HAwOdehGYkr+zt7KKdmCcJcjH0mYg6oL8+ioT3Skjmt7BLoAQqXhjf40AXd+HlR4oAWMlXjA==} + dev: true + + /elliptic@6.5.4: + resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + dev: false + + /emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /engine.io-client@6.5.2: + resolution: {integrity: sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==} + dependencies: + '@socket.io/component-emitter': 3.1.0 + debug: 4.3.4 + engine.io-parser: 5.2.1 + ws: 8.11.0 + xmlhttprequest-ssl: 2.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + + /engine.io-parser@5.2.1: + resolution: {integrity: sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==} + engines: {node: '>=10.0.0'} + dev: false + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-google@0.14.0(eslint@8.51.0): + resolution: {integrity: sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==} + engines: {node: '>=0.10.0'} + peerDependencies: + eslint: '>=5.16.0' + dependencies: + eslint: 8.51.0 + dev: true + + /eslint-config-prettier@9.0.0(eslint@8.50.0): + resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.50.0 + dev: true + + /eslint-plugin-es@3.0.1(eslint@8.50.0): + resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=4.19.1' + dependencies: + eslint: 8.50.0 + eslint-utils: 2.1.0 + regexpp: 3.2.0 + dev: true + + /eslint-plugin-jest@27.4.2(eslint@8.51.0)(jest@29.7.0)(typescript@5.1.6): + resolution: {integrity: sha512-3Nfvv3wbq2+PZlRTf2oaAWXWwbdBejFRBR2O8tAO67o+P8zno+QGbcDYaAXODlreXVg+9gvWhKKmG2rgfb8GEg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.0.0 || ^6.0.0 + eslint: ^7.0.0 || ^8.0.0 + jest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + jest: + optional: true + dependencies: + '@typescript-eslint/utils': 5.62.0(eslint@8.51.0)(typescript@5.1.6) + eslint: 8.51.0 + jest: 29.7.0(@types/node@20.8.5)(ts-node@10.9.1) + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /eslint-plugin-node@11.1.0(eslint@8.50.0): + resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=5.16.0' + dependencies: + eslint: 8.50.0 + eslint-plugin-es: 3.0.1(eslint@8.50.0) + eslint-utils: 2.1.0 + ignore: 5.2.4 + minimatch: 3.1.2 + resolve: 1.22.6 + semver: 6.3.1 + dev: true + + /eslint-plugin-prettier@5.0.0(eslint-config-prettier@9.0.0)(eslint@8.50.0)(prettier@3.0.3): + resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.50.0 + eslint-config-prettier: 9.0.0(eslint@8.50.0) + prettier: 3.0.3 + prettier-linter-helpers: 1.0.0 + synckit: 0.8.5 + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-utils@2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + dependencies: + eslint-visitor-keys: 1.3.0 + dev: true + + /eslint-visitor-keys@1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.50.0: + resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@eslint-community/regexpp': 4.9.1 + '@eslint/eslintrc': 2.1.2 + '@eslint/js': 8.50.0 + '@humanwhocodes/config-array': 0.11.11 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.22.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint@8.51.0: + resolution: {integrity: sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) + '@eslint-community/regexpp': 4.9.1 + '@eslint/eslintrc': 2.1.2 + '@eslint/js': 8.51.0 + '@humanwhocodes/config-array': 0.11.11 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.22.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.10.0 + acorn-jsx: 5.3.2(acorn@8.10.0) + eslint-visitor-keys: 3.4.3 + dev: true + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /ethereum-cryptography@0.1.3: + resolution: {integrity: sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==} + dependencies: + '@types/pbkdf2': 3.1.0 + '@types/secp256k1': 4.0.4 + blakejs: 1.2.1 + browserify-aes: 1.2.0 + bs58check: 2.1.2 + create-hash: 1.2.0 + create-hmac: 1.1.7 + hash.js: 1.1.7 + keccak: 3.0.4 + pbkdf2: 3.1.2 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + scrypt-js: 3.0.1 + secp256k1: 4.0.3 + setimmediate: 1.0.5 + dev: false + + /ethereumjs-util@7.1.5: + resolution: {integrity: sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==} + engines: {node: '>=10.0.0'} + dependencies: + '@types/bn.js': 5.1.2 + bn.js: 5.2.1 + create-hash: 1.2.0 + ethereum-cryptography: 0.1.3 + rlp: 2.2.7 + dev: false + + /evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + dev: false + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + + /exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + dev: true + + /expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + dev: true + + /external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + dependencies: + bser: 2.1.1 + dev: true + + /figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.1.0 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.1.0: + resolution: {integrity: sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==} + engines: {node: '>=12.0.0'} + dependencies: + flatted: 3.2.9 + keyv: 4.5.3 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + /follow-redirects@1.15.3: + resolution: {integrity: sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + + /fs-extra@11.1.1: + resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /git-raw-commits@2.0.11: + resolution: {integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + dargs: 7.0.0 + lodash: 4.17.21 + meow: 8.1.2 + split2: 3.2.2 + through2: 4.0.2 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /global-dirs@0.1.1: + resolution: {integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==} + engines: {node: '>=4'} + dependencies: + ini: 1.3.8 + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals@13.22.0: + resolution: {integrity: sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /gts@5.2.0(typescript@5.1.6): + resolution: {integrity: sha512-25qOnePUUX7upFc4ycqWersDBq+o1X6hXUTW56JOWCxPYKJXQ1RWzqT9q+2SU3LfPKJf+4sz4Dw3VT0p96Kv6g==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + typescript: '>=3' + dependencies: + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.50.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.62.0(eslint@8.51.0)(typescript@5.1.6) + chalk: 4.1.2 + eslint: 8.50.0 + eslint-config-prettier: 9.0.0(eslint@8.50.0) + eslint-plugin-node: 11.1.0(eslint@8.50.0) + eslint-plugin-prettier: 5.0.0(eslint-config-prettier@9.0.0)(eslint@8.50.0)(prettier@3.0.3) + execa: 5.1.1 + inquirer: 7.3.3 + json5: 2.2.3 + meow: 9.0.0 + ncp: 2.0.0 + prettier: 3.0.3 + rimraf: 3.0.2 + typescript: 5.1.6 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - '@types/eslint' + - supports-color + dev: true + + /hard-rejection@2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + safe-buffer: 5.2.1 + dev: false + + /hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + dev: false + + /hdkey@2.1.0: + resolution: {integrity: sha512-i9Wzi0Dy49bNS4tXXeGeu0vIcn86xXdPQUpEYg+SO1YiO8HtomjmmRMaRyqL0r59QfcD4PfVbSF3qmsWFwAemA==} + dependencies: + bs58check: 2.1.2 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + secp256k1: 4.0.3 + dev: false + + /hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + dev: false + + /hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: true + + /hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + dependencies: + lru-cache: 6.0.0 + dev: true + + /html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + dev: true + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + dev: true + + /husky@8.0.3: + resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-local@3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + /inquirer@7.3.3: + resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} + engines: {node: '>=8.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + run-async: 2.4.1 + rxjs: 6.6.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-core-module@2.13.0: + resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} + dependencies: + has: 1.0.3 + dev: true + + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + + /is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + dependencies: + is-docker: 3.0.0 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-plain-obj@1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-text-path@1.0.1: + resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} + engines: {node: '>=0.10.0'} + dependencies: + text-extensions: 1.9.0 + dev: true + + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /istanbul-lib-coverage@3.2.0: + resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} + engines: {node: '>=8'} + dev: true + + /istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.23.0 + '@babel/parser': 7.23.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-instrument@6.0.0: + resolution: {integrity: sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==} + engines: {node: '>=10'} + dependencies: + '@babel/core': 7.23.0 + '@babel/parser': 7.23.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.0 + make-dir: 4.0.0 + supports-color: 7.2.0 + dev: true + + /istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + dependencies: + debug: 4.3.4 + istanbul-lib-coverage: 3.2.0 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-reports@3.1.6: + resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + dev: true + + /jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + dev: true + + /jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.8.5 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.1 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.0.4 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-cli@29.7.0(@types/node@20.8.5)(ts-node@10.9.1): + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.1) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.8.5)(ts-node@10.9.1) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@20.8.5)(ts-node@10.9.1) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /jest-config@29.7.0(@types/node@20.8.5)(ts-node@10.9.1): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.23.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.8.5 + babel-jest: 29.7.0(@babel/core@7.23.0) + chalk: 4.1.2 + ci-info: 3.8.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + ts-node: 10.9.1(@types/node@20.8.5)(typescript@5.1.6) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.8.5 + jest-mock: 29.7.0 + jest-util: 29.7.0 + dev: true + + /jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.7 + '@types/node': 20.8.5 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.22.13 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + dev: true + + /jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.8.5 + jest-util: 29.7.0 + dev: true + + /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.7.0 + dev: true + + /jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.6 + resolve.exports: 2.0.2 + slash: 3.0.0 + dev: true + + /jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.8.5 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.8.5 + chalk: 4.1.2 + cjs-module-lexer: 1.2.3 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.23.0 + '@babel/generator': 7.23.0 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.0) + '@babel/types': 7.23.0 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.0) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.8.5 + chalk: 4.1.2 + ci-info: 3.8.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + dev: true + + /jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + dev: true + + /jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.8.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + dev: true + + /jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@types/node': 20.8.5 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest@29.7.0(@types/node@20.8.5)(ts-node@10.9.1): + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.1) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@20.8.5)(ts-node@10.9.1) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-buffer@2.0.11: + resolution: {integrity: sha512-Wu4/hxSZX7Krzjor+sZHWaRau6Be4WQHlrkl3v8cmxRBBewF2TotlgHUedKQJyFiUyFxnK/ZlRYnR9UNVZ7pkg==} + dev: false + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.0 + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + + /jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + dev: true + + /keccak@3.0.4: + resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==} + engines: {node: '>=10.0.0'} + requiresBuild: true + dependencies: + node-addon-api: 2.0.2 + node-gyp-build: 4.6.1 + readable-stream: 3.6.2 + dev: false + + /keyv@4.5.3: + resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: true + + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: true + + /leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + dev: true + + /lodash.isfunction@3.0.9: + resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} + dev: true + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: true + + /lodash.kebabcase@4.1.1: + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + dev: true + + /lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + dev: true + + /lodash.snakecase@4.1.1: + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + dev: true + + /lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + dev: true + + /lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + dev: true + + /lodash.upperfirst@4.3.1: + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /long@3.2.0: + resolution: {integrity: sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg==} + engines: {node: '>=0.6'} + dev: false + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 + dev: true + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + dependencies: + tmpl: 1.0.5 + dev: true + + /map-obj@1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + dev: true + + /map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + dev: true + + /md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + + /meow@8.1.2: + resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} + engines: {node: '>=10'} + dependencies: + '@types/minimist': 1.2.3 + camelcase-keys: 6.2.2 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.18.1 + yargs-parser: 20.2.9 + dev: true + + /meow@9.0.0: + resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==} + engines: {node: '>=10'} + dependencies: + '@types/minimist': 1.2.3 + camelcase-keys: 6.2.2 + decamelize: 1.2.0 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.18.1 + yargs-parser: 20.2.9 + dev: true + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + dev: false + + /minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + dev: false + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist-options@4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + dev: true + + /natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /ncp@2.0.0: + resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==} + hasBin: true + dev: true + + /node-addon-api@2.0.2: + resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} + dev: false + + /node-gyp-build@4.6.1: + resolution: {integrity: sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==} + hasBin: true + dev: false + + /node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + dev: true + + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: true + + /normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.6 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + dev: true + + /normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.13.0 + semver: 7.5.4 + validate-npm-package-license: 3.0.4 + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /open@9.1.0: + resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} + engines: {node: '>=14.16'} + dependencies: + default-browser: 4.0.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + is-wsl: 2.2.0 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.22.13 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /pbkdf2@3.1.2: + resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + engines: {node: '>=0.12'} + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + dev: false + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: true + + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.3.0 + dev: true + + /prettier@3.0.3: + resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + + /proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: false + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + /pure-rand@6.0.4: + resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /quick-lru@4.0.1: + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} + dev: true + + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + dev: true + + /read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + dependencies: + '@types/normalize-package-data': 2.4.2 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + dev: true + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + /redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + dev: true + + /regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true + + /resolve-global@1.0.0: + resolution: {integrity: sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==} + engines: {node: '>=8'} + dependencies: + global-dirs: 0.1.1 + dev: true + + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + + /resolve@1.22.6: + resolution: {integrity: sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==} + hasBin: true + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + dev: false + + /rlp@2.2.7: + resolution: {integrity: sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==} + hasBin: true + dependencies: + bn.js: 5.2.1 + dev: false + + /run-applescript@5.0.0: + resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} + engines: {node: '>=12'} + dependencies: + execa: 5.1.1 + dev: true + + /run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /rxjs@6.6.7: + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} + dependencies: + tslib: 1.14.1 + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true + + /scrypt-js@3.0.1: + resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==} + dev: false + + /secp256k1@4.0.3: + resolution: {integrity: sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==} + engines: {node: '>=10.0.0'} + requiresBuild: true + dependencies: + elliptic: 6.5.4 + node-addon-api: 2.0.2 + node-gyp-build: 4.6.1 + dev: false + + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + dev: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + dev: false + + /sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /socket.io-client@4.7.2: + resolution: {integrity: sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==} + engines: {node: '>=10.0.0'} + dependencies: + '@socket.io/component-emitter': 3.1.0 + debug: 4.3.4 + engine.io-client: 6.5.2 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + + /socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + dependencies: + '@socket.io/component-emitter': 3.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + + /sodium-browserify-tweetnacl@0.2.6: + resolution: {integrity: sha512-ZnEI26hdluilpYY28Xc4rc1ALfmEp2TWihkJX6Mdtw0z9RfHfpZJU7P8DoKbN1HcBdU9aJmguFZs7igE8nLJPg==} + dependencies: + chloride-test: 1.2.4 + ed2curve: 0.1.4 + sha.js: 2.4.11 + tweetnacl: 1.0.3 + tweetnacl-auth: 0.3.1 + dev: false + + /sodium-native@3.2.1: + resolution: {integrity: sha512-EgDZ/Z7PxL2kCasKk7wnRkV8W9kvwuIlHuHXAxkQm3FF0MgVsjyLBXGjSRGhjE6u7rhSpk3KaMfFM23bfMysIQ==} + requiresBuild: true + dependencies: + ini: 1.3.8 + node-gyp-build: 4.6.1 + dev: false + optional: true + + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.15 + dev: true + + /spdx-exceptions@2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.15 + dev: true + + /spdx-license-ids@3.0.15: + resolution: {integrity: sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==} + dev: true + + /split2@3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + dependencies: + readable-stream: 3.6.2 + dev: true + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + + /stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + dependencies: + escape-string-regexp: 2.0.0 + dev: true + + /string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + dev: true + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /synckit@0.8.5: + resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} + engines: {node: ^14.18.0 || >=16.0.0} + dependencies: + '@pkgr/utils': 2.4.2 + tslib: 2.6.2 + dev: true + + /test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + + /text-extensions@1.9.0: + resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} + engines: {node: '>=0.10'} + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /through2@4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + dependencies: + readable-stream: 3.6.2 + dev: true + + /through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: true + + /tiny-secp256k1@2.2.3: + resolution: {integrity: sha512-SGcL07SxcPN2nGKHTCvRMkQLYPSoeFcvArUSCYtjVARiFAWU44cCIqYS0mYAU6nY7XfvwURuTIGo2Omt3ZQr0Q==} + engines: {node: '>=14.0.0'} + dependencies: + uint8array-tools: 0.0.7 + dev: false + + /titleize@3.0.0: + resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} + engines: {node: '>=12'} + dev: true + + /tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + dependencies: + os-tmpdir: 1.0.2 + dev: true + + /tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /trim-newlines@3.0.1: + resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} + engines: {node: '>=8'} + dev: true + + /ts-jest@29.1.1(@babel/core@7.23.0)(jest@29.7.0)(typescript@5.1.6): + resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.23.0 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@20.8.5)(ts-node@10.9.1) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.5.4 + typescript: 5.1.6 + yargs-parser: 21.1.1 + dev: true + + /ts-node@10.9.1(@types/node@20.8.5)(typescript@5.1.6): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.8.5 + acorn: 8.10.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.1.6 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: true + + /tsutils@3.21.0(typescript@5.1.6): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.1.6 + dev: true + + /tweetnacl-auth@0.3.1: + resolution: {integrity: sha512-9/c8c6qRMTfWuv54ETFhihgYoofi0M9HUovMSGJ1rLRUj6O5A0vuCg2L/qKfvmcjLVhaTgAJCLy2EqCLJK2QLw==} + dependencies: + tweetnacl: 0.14.5 + dev: false + + /tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + dev: false + + /tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.18.1: + resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + dev: true + + /type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + dev: true + + /typeforce@1.18.0: + resolution: {integrity: sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==} + dev: false + + /typescript@5.1.6: + resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /uint8array-tools@0.0.7: + resolution: {integrity: sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ==} + engines: {node: '>=14.0.0'} + dev: false + + /undici-types@5.25.3: + resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} + + /universalify@2.0.0: + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + engines: {node: '>= 10.0.0'} + dev: true + + /untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.22.1): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.1 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /v8-to-istanbul@9.1.0: + resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.19 + '@types/istanbul-lib-coverage': 2.0.4 + convert-source-map: 1.9.0 + dev: true + + /validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + dev: true + + /varuint-bitcoin@1.1.2: + resolution: {integrity: sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + dependencies: + makeerror: 1.0.12 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wif@2.0.6: + resolution: {integrity: sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==} + dependencies: + bs58check: 2.1.2 + dev: false + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + dev: true + + /ws@8.11.0: + resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + + /xmlhttprequest-ssl@2.0.0: + resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==} + engines: {node: '>=0.4.0'} + dev: false + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/src/api/generated.ts b/src/api/generated.ts new file mode 100644 index 0000000..4bebb47 --- /dev/null +++ b/src/api/generated.ts @@ -0,0 +1,932 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +/** @example {"account":{"address":"U777355171330060015","unconfirmedBalance":"4509718944753","balance":"4509718944753","publicKey":"a9407418dafb3c8aeee28f3263fd55bae0f528a5697a9df0e77e6568b19dfe34","unconfirmedSignature":0,"secondSignature":0,"secondPublicKey":null,"multisignatures":[],"u_multisignatures":[]},"success":true,"nodeTimestamp":58030181} */ +export interface GetAccountInfoResponseDto { + account: AccountDto; + success: boolean; + nodeTimestamp: number; +} + +/** @example {"balance":"4509718944753","unconfirmedBalance":"4509718944753","success":true,"nodeTimestamp":58043820} */ +export interface GetAccountBalanceResponseDto { + /** @format int64 */ + balance: string; + /** @format int64 */ + unconfirmedBalance: string; + success: boolean; + nodeTimestamp: number; +} + +/** @example {"publicKey":"a9407418dafb3c8aeee28f3263fd55bae0f528a5697a9df0e77e6568b19dfe34","balance":"4509718944753","unconfirmedBalance":"4509718944753"} */ +export interface GetAccountPublicKeyResponseDto { + /** 256 bit public key of ADAMANT address in hex format */ + publicKey: PublicKey; + success: boolean; + nodeTimestamp: number; +} + +/** @example {"publicKey":"a9407418dafb3c8aeee28f3263fd55bae0f528a5697a9df0e77e6568b19dfe34","balance":"4509718944753","unconfirmedBalance":"4509718944753"} */ +export interface CreateNewAccountRequestBody { + /** 256 bit public key of ADAMANT address in hex format */ + publicKey: PublicKey; + success: boolean; + nodeTimestamp: number; +} + +/** @example {"account":{"address":"U4697606961271319613","unconfirmedBalance":"0","balance":"0","publicKey":"bee368cc0ce2974adcbcc97e649ac18a031492a579034abed5f77d667001d450","unconfirmedSignature":0,"secondSignature":0,"secondPublicKey":null,"multisignatures":null,"u_multisignatures":null},"success":true,"nodeTimestamp":63205623} */ +export interface CreateNewAccountResponseDto { + account: AccountDto; + success: boolean; + nodeTimestamp: number; +} + +export interface GetBlockInfoResponseDto { + block: BlockInfoDto; + success: boolean; + nodeTimestamp: number; +} + +export interface GetBlocksResponseDto { + blocks: BlockInfoDto[]; + success: boolean; + nodeTimestamp: number; +} + +export interface GetChatRoomsResponseDto { + chats: { + lastTransaction?: TokenTransferTransaction | ChatMessageTransaction; + participants?: ChatParticipant[]; + }[]; + success: boolean; + nodeTimestamp: number; +} + +export interface GetChatMessagesResponseDto { + messages: (TokenTransferTransaction | ChatMessageTransaction)[]; + participants: ChatParticipant[]; + success: boolean; + nodeTimestamp: number; +} + +export interface GetChatTransactionsResponseDto { + transactions: ChatMessageTransaction[]; + /** Number in string format */ + count: string; + success: boolean; + nodeTimestamp: number; +} + +export interface CreateNewChatMessageRequestBody { + transaction: RegisterChatMessageTransaction; +} + +/** @example {"success":true,"nodeTimestamp":63205623,"transactionId":"2515012750420367858"} */ +export interface CreateNewChatMessageResponseDto { + success: boolean; + nodeTimestamp: number; + transactionId: string; +} + +export interface GetDelegatesResponseDto { + delegates: DelegateDto[]; + /** @example 254 */ + totalCount: number; + /** @example true */ + success: boolean; + /** @example 61762271 */ + nodeTimestamp: number; +} + +export interface GetDelegateResponseDto { + delegate: DelegateDto; + /** @example true */ + success: boolean; + /** @example 61762271 */ + nodeTimestamp: number; +} + +export type RegisterDelegateRequestBody = RegisterNewDelegateTransaction; + +export interface RegisterDelegateResponseDto { + transaction: RegisterDelegateTransactionDto; + /** @example true */ + success: boolean; + /** @example 61762271 */ + nodeTimestamp: number; +} + +export interface SearchDelegateResponseDto { + delegates: SearchDelegateDto[]; + /** @example true */ + success: boolean; + /** @example 61762271 */ + nodeTimestamp: number; +} + +export interface GetDelegatesCountResponseDto { + /** @example 255 */ + count: number; + /** @example true */ + success: boolean; + /** @example 61762271 */ + nodeTimestamp: number; +} + +export interface GetDelegateStatsResponseDto { + /** + * Total sum of fees forged by delegate + * @example "586039475511" + */ + fees: string; + /** + * Total sum of rewards made by delegate + * @example "3943485000000" + */ + rewards: string; + /** + * Total sum of forged tokens + * @example "4529524475511" + */ + forged: string; + /** @example true */ + success: boolean; + /** @example 61762271 */ + nodeTimestamp: number; +} + +export interface GetNextForgersResponseDto { + /** + * Array of next forgers public keys + * @example ["677c6db63548c99674fed0571da522a6a9569d0c1da9669734a3625645519641","150d638714f65845b50f1ff58f3da2c2baa3a1dc8bf59a9884c10da5a8e951c6"] + */ + delegates: string[]; + /** + * Current slot number + * @example 11610423 + */ + currentSlot: number; + /** + * Current blockchain height + * @example 10146268 + */ + currentBlock: number; + /** + * Current block slot number + * @example 11610422 + */ + currentBlockSlot: number; + /** @example true */ + success: boolean; + /** @example 61762271 */ + nodeTimestamp: number; +} + +export interface GetVotersResponseDto { + accounts: VoterDto[]; + /** @example true */ + success: boolean; + /** @example 61762271 */ + nodeTimestamp: number; +} + +export interface GetAccountVotesResponseDto { + /** List of delegates account voted for. */ + delegates: DelegateDto[]; + /** @example true */ + success: boolean; + /** @example 61762271 */ + nodeTimestamp: number; +} + +export type RegisterVotesRequestBody = RegisterVoteForDelegateTransaction; + +export interface RegisterVotesResponseDto { + transaction: RegisterVotesTransactionDto; + /** @example true */ + success: boolean; + /** @example 61762271 */ + nodeTimestamp: number; +} + +export interface GetPeersResponseDto { + peers: PeerDto[]; + success: boolean; + nodeTimestamp: number; +} + +/** @example {"loaded":true,"now":10144343,"blocksCount":0,"success":true,"nodeTimestamp":58052355} */ +export interface GetLoadingStatusResponseDto { + loaded: boolean; + now: number; + blocksCount: number; + success: boolean; + nodeTimestamp: number; +} + +/** @example {"success":true,"nodeTimestamp":58052355,"syncing":false,"blocks":0,"height":10146332,"broadhash":"09f2f5614cf7209979dc1df2dd92d16aade904dae6c9b68bccaeb234647b3c18","consensus":94.32} */ +export interface GetSyncStatusResponseDto { + success: boolean; + nodeTimestamp: number; + syncing: boolean; + blocks: number; + height: number; + broadhash: string; + consensus: number; +} + +/** @example {"success":true} */ +export interface GetPingStatusResponseDto { + success: boolean; +} + +export type GetNodeVersionResponseDto = NodeVersion & { + /** @example true */ + success: boolean; + /** @example 58052984 */ + nodeTimestamp: number; +}; + +/** @example {"success":true,"nodeTimestamp":58052355,"broadhash":"e1aedd2818679c174e3f6e31891c34f4069927f33f145e1b81fe5d978733e794"} */ +export interface GetBroadhashResponseDto { + success: boolean; + nodeTimestamp: number; + /** Broadhash is established as an aggregated rolling hash of the past five blocks present in the database */ + broadhash: string; +} + +/** @example {"success":true,"nodeTimestamp":58646306,"epoch":"2017-09-02T17:00:00.000Z"} */ +export interface GetEpochResponseDto { + success: boolean; + nodeTimestamp: number; + /** Time when blockchain epoch starts. Value `2017-09-02T17:00:00.000Z` is for mainnet. */ + epoch: string; +} + +/** @example {"success":true,"nodeTimestamp":58646306,"height":10145318} */ +export interface GetHeightResponseDto { + success: boolean; + nodeTimestamp: number; + /** Current node height. */ + height: number; +} + +/** @example {"success":true,"nodeTimestamp":58646306,"fee":50000000} */ +export interface GetTokenTransferFeeResponseDto { + success: boolean; + nodeTimestamp: number; + /** Current fee value for `type 0` (token transfer) transactions. Integer amount of 1/10^8 ADM tokens (1 ADM = 100000000). */ + fee: number; +} + +export interface GetTransactionTypesFeesResponseDto { + /** @example true */ + success: boolean; + /** @example 58646306 */ + nodeTimestamp: number; + fees: TransactionTypesFeesDto; +} + +/** @example {"success":true,"nodeTimestamp":58646306,"nethash":"bd330166898377fb28743ceef5e43a5d9d0a3efd9b3451fb7bc53530bb0a6d64"} */ +export interface GetNethashResponseDto { + success: boolean; + nodeTimestamp: number; + /** The `nethash` describes e.g. the Mainnet or the Testnet, that the node is connecting to. */ + nethash: string; +} + +/** @example {"success":true,"nodeTimestamp":58646306,"milestone":1} */ +export interface GetMilestoneResponseDto { + success: boolean; + nodeTimestamp: number; + /** Current slot height, which determines reward a delegate will get for forging a block. */ + milestone: number; +} + +/** @example {"success":true,"nodeTimestamp":58646306,"reward":45000000} */ +export interface GetRewardResponseDto { + success: boolean; + nodeTimestamp: number; + /** The reward a delegate will get for forging a block. Integer amount of 1/10^8 ADM tokens (1 ADM = 100000000). Depends on the slot height. */ + reward: number; +} + +/** @example {"success":true,"nodeTimestamp":58646306,"supply":10198038140000000} */ +export interface GetTokensTotalSupplyResponseDto { + success: boolean; + nodeTimestamp: number; + /** Total current supply of ADM tokens in the network. Integer amount of 1/10^8 ADM tokens (1 ADM = 100000000). Total supply increases with every new forged block. */ + supply: number; +} + +export type GetNetworkInfoResponseDto = NetworkStatus & { + success: boolean; + nodeTimestamp: number; +}; + +export interface GetNodeStatusResponseDto { + /** @example true */ + success: boolean; + /** @example 58052984 */ + nodeTimestamp: number; + network: NetworkStatus; + version: NodeVersion; + wsClient: WsClient; +} + +export interface GetKVSResponseDto { + transactions: KVSTransaction[]; + /** + * Integer in string format + * @example "1" + */ + count: string; + /** @example true */ + success: boolean; + /** @example 63647706 */ + nodeTimestamp: number; +} + +export interface SetKVSRequestBody { + transaction: RegisterKVSTransaction; +} + +/** @example {"success":true,"nodeTimestamp":63410860,"transactionId":"3888802408802922744"} */ +export interface SetKVSResponseDto { + success: boolean; + nodeTimestamp: number; + transactionId: string; +} + +export interface GetTransactionsResponseDto { + transactions: AnyTransaction[]; + /** + * Integer in string format + * @example "1" + */ + count: string; + /** @example true */ + success: boolean; + /** @example 63647706 */ + nodeTimestamp: number; +} + +export interface GetTransactionByIdResponseDto { + transaction: AnyTransaction; + /** @example true */ + success: boolean; + /** @example 63647706 */ + nodeTimestamp: number; +} + +/** @example {"success":true,"nodeTimestamp":59979539,"confirmed":256953,"unconfirmed":44,"queued":4,"multisignature":0} */ +export interface GetTransactionsCountResponseDto { + success: boolean; + nodeTimestamp: number; + confirmed: number; + unconfirmed: number; + queued: number; + multisignature: number; +} + +export interface GetQueuedTransactionsResponseDto { + transactions: QueuedTransaction[]; + /** + * Integer in string format + * @example "1" + */ + count: string; + /** @example true */ + success: boolean; + /** @example 63647706 */ + nodeTimestamp: number; +} + +export interface GetQueuedTransactionByIdResponseDto { + transaction: QueuedTransaction; + /** @example true */ + success: boolean; + /** @example 63647706 */ + nodeTimestamp: number; +} + +export interface GetUnconfirmedTransactionsResponseDto { + transactions: QueuedTransaction[]; + /** + * Integer in string format + * @example "1" + */ + count: string; + /** @example true */ + success: boolean; + /** @example 63647706 */ + nodeTimestamp: number; +} + +export interface GetUnconfirmedTransactionByIdResponseDto { + transaction: QueuedTransaction; + /** @example true */ + success: boolean; + /** @example 63647706 */ + nodeTimestamp: number; +} + +export interface TransferTokenRequestBody { + transaction: RegisterTokenTransferTransaction; +} + +export interface TransferTokenResponseDto { + /** @example "6146865104403680934" */ + transactionId: string; + /** @example true */ + success: boolean; + /** @example 63647706 */ + nodeTimestamp: number; +} + +export interface RegisterTransactionRequestBody { + transaction: RegisterAnyTransaction; +} + +export interface RegisterTransactionResponseDto { + /** @example "6146865104403680934" */ + transactionId: string; + /** @example true */ + success: boolean; + /** @example 63647706 */ + nodeTimestamp: number; +} + +/** + * 256 bit public key of ADAMANT address in hex format + * @example "ef5e78a3d02e6d82f4ac0c5b8923c1b86185bd17c27c9ac027c20ec62db79a84" + */ +export type PublicKey = string; + +export interface AccountDto { + address: string; + /** @format int64 */ + unconfirmedBalance: string; + /** @format int64 */ + balance: string; + /** 256 bit public key of ADAMANT address in hex format */ + publicKey: PublicKey; + unconfirmedSignature: number; + secondSignature: number; + secondPublicKey: string | null; + /** @example [] */ + multisignatures: string[]; + /** @example [] */ + u_multisignatures: string[]; +} + +/** @example {"id":"11114690216332606721","version":0,"timestamp":61741820,"height":10873829,"previousBlock":"11483763337863654141","numberOfTransactions":1,"totalAmount":10000000,"totalFee":50000000,"reward":45000000,"payloadLength":117,"payloadHash":"f7c0fa338a3a848119cad999d8035ab3fcb3d274a4555e141ebeb86205e41345","generatorPublicKey":"134a5de88c7da1ec71e75b5250d24168c6c6e3965ff16bd71497bd015d40ea6a","generatorId":"U3238410389688281135","blockSignature":"18607b15417a6b0a56b4c74cacd713ad7a10df16ec3ab45a697fa72b6f811f9213d895b7e0fbca71cf74323d60148d0991668e5368386408f4d841496ed2280d","confirmations":1093,"totalForged":"95000000"} */ +export interface BlockInfoDto { + /** @format int64 */ + id: string; + version: number; + timestamp: number; + height: number; + /** @format int64 */ + previousBlock: string; + numberOfTransactions: number; + totalAmount: number; + totalFee: number; + reward: number; + payloadLength: number; + payloadHash: string; + generatorId: string; + blockSignature: string; + confirmations: number; + /** @format int64 */ + totalForged: string; +} + +export interface BaseTransaction { + id: string; + height: number; + blockId: string; + /** + * Type of transaction. See [Transaction Types](https://github.com/Adamant-im/adamant/wiki/Transaction-Types). + * @min 0 + * @max 9 + * @example 0 + */ + type: number; + block_timestamp: number; + timestamp: number; + senderPublicKey: string; + senderId: string; + recipientId: string; + recipientPublicKey: string; + /** + * Amount to transfer, 8 decimal points (100000000 equals to 1 ADM). For non-transfer transactions must be `0` + * @format int64 + */ + amount: number; + /** Fee for operation. Depends on [Transaction Type](https://github.com/Adamant-im/adamant/wiki/Transaction-Types) */ + fee: number; + signature: string; + signatures: string[]; + confirmations: number; + /** @example {} */ + asset: object; +} + +/** + * An empty object + * @example {} + */ +export type TokenTransferAsset = object; + +/** @example {"id":16682447412632443000,"height":10527806,"blockId":2635215585577611300,"type":0,"block_timestamp":59979295,"timestamp":59979276,"senderPublicKey":"632816f2c44a08f282e85532443d73286cadc6d9820d5d25c9d50d8e01c668e0","senderId":"U17362714543155685887","recipientId":"U17819800352812315500","recipientPublicKey":"28994b2cd075fd442e6ce78fa8c07966ed122932ff07411fed3c918e495586e2","amount":100000000,"fee":50000000,"signature":"1db7e9111eaca790b73d51c32572739c46fcba3962aff55ca47ecf9a8c9fcb82c323de39ed60bc87d81a1245d43b5351b9dd44ad70128d78536250168b64c408","signatures":[],"confirmations":18431929,"asset":{}} */ +export type TokenTransferTransaction = BaseTransaction & { + /** + * Always equal to `0` + * @example 0 + */ + type: 0; + /** An empty object */ + asset: TokenTransferAsset; +}; + +/** @example {"chat":{"message":"748e4e9cffc969dfa4c1d7b9b708cb171c9e","own_message":"96904970891b838c9a3ab1b9a6f31ec194ec94ffaa95d0cd","type":1}} */ +export interface ChatMessageAsset { + chat: { + /** Encrypted message */ + message: string; + /** Nonce */ + own_message: string; + /** Type of chat message (1 - Basic Encrypted Message, 2 - Rich Content Message, 3 - Signal Message). See details https://github.com/Adamant-im/adamant/wiki/Message-Types */ + type: 1 | 2 | 3; + }; +} + +/** @example {"id":17242227802580636000,"height":7583081,"blockId":10363608465961390000,"type":8,"block_timestamp":64874935,"timestamp":64874929,"senderPublicKey":"b34d48d8d70b3a91f766df34789abf0cad62da7207e171d997508a460217c5d3","senderId":"U13267906643444995032","recipientId":"U9203183357885757380","recipientPublicKey":"741d3d1f52e609eef981e9ab370ec1e7c3ff70cafad94691937a2bb6d84bbff2","amount":0,"fee":100000,"signature":"8803346cf43457aba3480311ee489706ec66493fa043c4d1732682eb86e88d96f36a7e87c1d0d00dd3963f75e763e5554df402ee0aa79bd59bd55185a6e49a03","signatures":[],"confirmations":23229462,"asset":{"chat":{"message":"2f045f1d4a5198843999e2948b0cc78806","own_message":"a7cd3fa21e543dcc9f0564387d83c4d862137a2da37f29d4","type":1}}} */ +export type ChatMessageTransaction = BaseTransaction & { + /** + * Always equal to `8` + * @example 8 + */ + type: 8; + asset: ChatMessageAsset; +}; + +/** + * ADAMANT address + * @example "U8916295525136600565" + */ +export type AdamantAddress = string; + +export interface ChatParticipant { + /** ADAMANT address */ + address: AdamantAddress; + /** 256 bit public key of ADAMANT address in hex format */ + publicKey: PublicKey; +} + +/** @example {"type":0,"amount":0,"senderId":"U14236667426471084862","senderPublicKey":"8cd9631f9f634a361ea3b85cbd0df882633e39e7d26d7bc615bbcf75e41524ef","signature":"b3982d603be8f0246fa663e9f012bf28b198cd28f82473db1eb4a342d890f7a2a2c1845db8d256bb5bce1e64a9425822a91e10bf960a2e0b55e20b4841e4ae0b","timestamp":63228852} */ +export interface RegisterTransactionBase { + type: number; + amount: number; + senderId: string; + senderPublicKey: string; + signature: string; + timestamp: number; +} + +export type RegisterChatMessageTransaction = RegisterTransactionBase & { + /** + * Always equal to `8` + * @example 8 + */ + type: 8; + recipientId: string; + asset: ChatMessageAsset; +}; + +/** @example {"username":"galaxy","address":"U17457189553820283321","publicKey":"7e26562594685ba12c0bb99ae80692947828afb71962d54634795d78b3ea7023","vote":"248994436803629","votesWeight":"83910064952101","producedblocks":269879,"missedblocks":567,"rate":10,"rank":10,"approval":0.76,"productivity":99.79} */ +export interface DelegateDto { + /** Unique delegate's nickname */ + username: string; + /** Delegate address */ + address: string; + /** Delegate Public Key */ + publicKey: string; + /** Vote weight (obsolete, not used) */ + vote: string; + /** Vote weight (Fair Delegate System) */ + votesWeight: string; + /** Count of produced blocks */ + producedlocks?: number; + /** Count of missed blocks */ + missedblocks: number; + /** Current position in the Delegates List */ + rate: number; + /** Current position in the Delegates List */ + rank: number; + /** Share of votes of all votes in the system */ + approval: number; + /** Productivity / Uptime of a delegate. If `0`, delegate is not active now */ + productivity: number; +} + +/** @example {"type":2,"amount":0,"senderId":"U3031563782805250428","senderPublicKey":"a339974effc141f302bd3589c603bdc9468dd66bcc424b60025b36999eb69ca3","signature":"c2e4a3ef7f0d363611a2b22b96feff269f1a0cbb61741a2ce55756bb9324826092fd9bff6348145e3cc384c097f101a493b9136da5236292ecf8b1ed6657dd01","timestamp":166805250,"asset":{"delegate":{"username":"kpeo","publicKey":"a339974effc141f302bd3589c603bdc9468dd66bcc424b60025b36999eb69ca3"}}} */ +export type RegisterNewDelegateTransaction = RegisterTransactionBase & { + /** + * Should be always equal to `2` + * @example 2 + */ + type: 2; + asset: { + delegate: { + username: string; + /** 256 bit public key of ADAMANT address in hex format */ + publicKey: PublicKey; + }; + }; +}; + +export interface QueuedTransaction { + /** @example "U14236667426471084862" */ + recipientId: string; + /** @example 0 */ + amount: number; + /** + * See [Transaction Types](https://github.com/Adamant-im/adamant/wiki/Transaction-Types) + * @min 0 + * @max 9 + * @example 3 + */ + type: number; + /** @example "U14236667426471084862" */ + senderId: string; + /** @example "8cd9631f9f634a361ea3b85cbd0df882633e39e7d26d7bc615bbcf75e41524ef" */ + senderPublicKey: string; + /** @example 63394407 */ + timestamp?: number; + /** @example "7f4f5d240fc66da1cbdb3fe291d6fcec006848236355aebe346fcd1e3ba500caeac1ed0af6f3d7f912a889a1bbedc1d7bab17b6ebd36386b81df78189ddf7c07" */ + signature: string; + /** @example "13616514419605573351" */ + id: string; + /** @example 5000000000 */ + fee: number; + /** @example 1 */ + relays: number; + /** + * Date and time in ISO 8601 format + * @example "2019-09-06T10:33:28.054Z" + */ + receivedAt: string; +} + +/** @example {"delegate":{"address":"U3031563782805250428","username":"kpeo","publicKey":"a339974effc141f302bd3589c603bdc9468dd66bcc424b60025b36999eb69ca3"}} */ +export interface RegisterDelegateAsset { + delegate: { + /** ADAMANT address */ + address: AdamantAddress; + username: string; + /** 256 bit public key of ADAMANT address in hex format */ + publicKey: PublicKey; + }; +} + +export type RegisterDelegateTransactionDto = QueuedTransaction & { + asset: RegisterDelegateAsset; + /** + * Always equal to `2` + * @example 2 + */ + type: 2; + /** + * Always equal to `300000000000` + * @example 300000000000 + */ + fee?: 300000000000; +}; + +export type SearchDelegateDto = DelegateDto & { + /** + * Number of accounts who voted for delegate + * @example 12 + */ + voters_cnt: number; + /** + * Epoch timestamp of when delegate was registered + * @example 45523238 + */ + register_timestamp: number; +}; + +/** @example {"username":"leg","address":"U12609717384103730908","publicKey":"559418798f67a81b7f893aa8eab1218b9838a6b0bcd2bc8968c6d490ae0d5d77","balance":"506697"} */ +export interface VoterDto { + /** Voter's delegate username. `null` if address is not a delegate. */ + username: string | null; + /** Voter's ADAMANT address */ + address: string; + /** Voter's ADAMANT public key */ + publicKey: string; + /** ADM balance of voter's ADAMANT wallet. Integer amount of 1/10^8 ADM tokens (1 ADM = 100000000) */ + balance: string; +} + +/** @example {"votes":["-c0c580c3fb89409f32181fef58935f286f0c1bbf61bd727084ed915b3a4bc95b","ac903ab58135cd5f0613a929d876953214d224034b73c33e63bc153d669447f4"]} */ +export interface VoteForDelegateAsset { + votes: string[]; +} + +export type RegisterVoteForDelegateTransaction = RegisterTransactionBase & { + asset: VoteForDelegateAsset; + /** + * ADAMANT address of account who votes. Same as `senderId` + * @example "U14236667426471084862" + */ + recipientId: string; + /** + * Should be always equal to `3` + * @example 3 + */ + type: 3; + /** + * ADAMANT address of account who votes. Same as `recipientId` + * @example "U14236667426471084862" + */ + senderId: string; +}; + +export type RegisterVotesTransactionDto = QueuedTransaction & { + asset: VoteForDelegateAsset; + /** + * Always equal to `3` + * @example 3 + */ + type: 3; + /** @example true */ + success: boolean; + /** @example 61762271 */ + nodeTimestamp: number; +}; + +/** @example {"ip":"194.32.79.175","port":36666,"state":2,"os":"linux4.15.0-36-generic","version":"0.4.0","broadhash":"3dfdf6c7bbaf7537eac9c70432f7ba1cae835b9b15e4ecd97e147616dde67e62","height":10146365,"clock":null,"updated":1562424199553,"nonce":"jxXV6g0sHJhmDubq"} */ +export interface PeerDto { + /** IPv4 address of node */ + ip: string; + /** Port number of ADAMANT node. `36666` for mainnet or `36667` for testnet */ + port: string; + /** State of the peer. Available values: Connected (2), Disconnected, Banned */ + state: number; + /** Node's operating system */ + os: string; + /** ADAMANT node software version */ + version: string; + /** Broadhash on the peer node. Broadhash is established as an aggregated rolling hash of the past five blocks present in the database. */ + broadhash: string; + /** Current node's blockchain height */ + height: number; + clock: string | null; + /** Unix timestamp based in ms, when peer updated */ + updated: number; + /** Unique Identifier for the peer. Random string. */ + nonce: string; +} + +/** @example {"build":"","commit":"b07aaf9580dffb5cc95cc65f303f6f1e5fca7d9c","version":"0.5.2"} */ +export interface NodeVersion { + build: string; + commit: string; + version: string; +} + +/** @example {"send":50000000,"vote":5000000000,"delegate":300000000000,"old_chat_message":500000,"chat_message":100000,"state_store":100000,"profile_update":5000000,"avatar_upload":10000000} */ +export interface TransactionTypesFeesDto { + /** Token transfer, type 0 */ + send: number; + /** Voting for delegate, type 3 */ + vote: number; + /** Registration of a new delegate, type 2 */ + delegate: number; + /** Sending a message (not used for now) */ + old_chat_message: number; + /** Sending a message, type 8 */ + chat_message: number; + /** Storing data in KVS, type 9 */ + state_store: number; + profile_update: number; + avatar_upload: number; +} + +/** @example {"broadhash":"4a28272c915f74d118120bb47db547a18a7512e1d48092c48be86939a6d45b89","epoch":"2017-09-02T17:00:00.000Z","height":10145334,"fee":50000000,"milestone":1,"nethash":"bd330166898377fb28743ceef5e43a5d9d0a3efd9b3451fb7bc53530bb0a6d64","reward":45000000,"supply":10198040075000000} */ +export interface NetworkStatus { + /** Broadhash is established as an aggregated rolling hash of the past five blocks present in the database */ + broadhash: string; + /** Time when blockchain epoch starts. Value `2017-09-02T17:00:00.000Z` is for mainnet. */ + epoch: string; + height: number; + fee: number; + /** Current slot height, which determines reward a delegate will get for forging a block. */ + milestone: number; + /** The `nethash` describes e.g. the Mainnet or the Testnet, that the node is connecting to. */ + nethash: string; + /** The reward a delegate will get for forging a block. Integer amount of 1/10^8 ADM tokens (1 ADM = 100000000). */ + reward: number; + /** Total current supply of ADM tokens in the network. Integer amount of 1/10^8 ADM tokens (1 ADM = 100000000). */ + supply: number; +} + +/** @example {"enabled":true,"port":36668} */ +export interface WsClient { + enabled: boolean; + port: number; +} + +/** @example {"state":{"key":"eth:address","value":"0x2391EEaEc07B927D2BA4Fa5cB3cE4b490Fa6fffC","type":0}} */ +export interface KVSTransactionAsset { + state: { + key: string; + value: string; + type: 0; + }; +} + +/** @example {"id":11325216963059857000,"height":3377231,"blockId":14121859709526400000,"type":9,"block_timestamp":23943500,"timestamp":23943500,"senderPublicKey":"ac903ab58135cd5f0613a929d876953214d224034b73c33e63bc153d669447f4","senderId":"U5517006347330072401","recipientId":null,"recipientPublicKey":null,"amount":0,"fee":100000,"signature":"4c3bcca1f6c921cef7ce07f4e641f668c5c0660bb6432335d5e2117c7a4d8378b352e7fa4fac3126bd7228f5b9ac5d57100bb161da02f7efc16df9f7e602b10d","signatures":[],"confirmations":7856415,"asset":{"state":{"key":"eth:address","value":"0x2391EEaEc07B927D2BA4Fa5cB3cE4b490Fa6fffC","type":0}}} */ +export type KVSTransaction = BaseTransaction & { + /** + * Always equal to `9` + * @example 9 + */ + type: 9; + /** There is no recipient for this type of transaction */ + recipientId?: string | null; + /** There is no recipient for this type of transaction */ + recipientPublicKey?: string | null; + asset: KVSTransactionAsset; +}; + +export type RegisterKVSTransaction = RegisterTransactionBase & { + /** + * Should be always equal to `9` (Store in KVS transaction type) + * @example 9 + */ + type: 9; + asset: KVSTransactionAsset; +}; + +/** @example {"id":14674137414602658000,"height":31536741,"blockId":15921349202793791000,"type":2,"block_timestamp":166805152,"timestamp":166805152,"senderPublicKey":"a339974effc141f302bd3589c603bdc9468dd66bcc424b60025b36999eb69ca3","senderId":"U3031563782805250428","recipientId":null,"recipientPublicKey":null,"amount":0,"fee":300000000000,"relays":1,"signature":"1833a86e24d57ad6dbd30c47924500a03096fd06076fafe5bca4f23ab4629268f3b1a58a1ce275356bc0b79f64a11b8abe9bec6c3d55202d6393327f9278910b","signatures":[],"confirmations":427,"asset":{"delegate":{"username":"kpeo","publicKey":"a339974effc141f302bd3589c603bdc9468dd66bcc424b60025b36999eb69ca3","address":"U3031563782805250428"}}} */ +export type RegisterDelegateTransaction = BaseTransaction & { + /** + * Always equal to `2` + * @example 2 + */ + type: 2; + /** There is no recipient for this type of transaction */ + recipientId?: string | null; + /** There is no recipient for this type of transaction */ + recipientPublicKey?: string | null; + asset: RegisterDelegateAsset; +}; + +/** @example {"id":9888167852341778000,"height":10488572,"blockId":16481510969712464000,"type":3,"block_timestamp":59782601,"timestamp":59782601,"senderPublicKey":"9560562121cdc41112a0b288101079346d9c67f5bbff1f4d5a29483258c9477a","senderId":"U9221911598904803004","recipientId":"U9221911598904803004","recipientPublicKey":"9560562121cdc41112a0b288101079346d9c67f5bbff1f4d5a29483258c9477a","amount":0,"fee":5000000000,"signature":"fe199a4a5790186c1c482c6f5c0de5b7baa0a66e4b97abcb96f47e197880ea8333dc57e1b497e32eabdb157ac834dbd85d58d7c550e8aabe208af79026279c04","signatures":[],"confirmations":745088,"asset":{"votes":["-c0c580c3fb89409f32181fef58935f286f0c1bbf61bd727084ed915b3a4bc95b"]},"votes":{"added":[],"deleted":["c0c580c3fb89409f32181fef58935f286f0c1bbf61bd727084ed915b3a4bc95b"]}} */ +export type VoteForDelegateTransaction = BaseTransaction & { + votes: { + /** List of Upvoted delegates */ + added?: string[]; + /** List of Downvoted delegates */ + deleted?: string[]; + }; + /** + * Always equal to `3` + * @example 3 + */ + type: 3; + asset: VoteForDelegateAsset; +}; + +export type AnyTransaction = + | TokenTransferTransaction + | RegisterDelegateTransaction + | VoteForDelegateTransaction + | ChatMessageTransaction + | KVSTransaction; + +export type RegisterTokenTransferTransaction = RegisterTransactionBase & { + /** Can be `type 0 — Token transfer` or `type 8 — Chat/Message`. */ + type: 0 | 8; + recipientId: string; + /** Amount of token to transfer */ + amount: number; +}; + +export type RegisterAnyTransaction = + | RegisterVoteForDelegateTransaction + | RegisterTokenTransferTransaction + | RegisterKVSTransaction + | RegisterChatMessageTransaction + | RegisterNewDelegateTransaction; diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..1a0020b --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,839 @@ +import axios, {AxiosError} from 'axios'; +import {NodeManager, NodeManagerOptions} from '../helpers/healthCheck'; +import {Logger, type CustomLogger, type LogLevel} from '../helpers/logger'; +import { + AdamantApiResult, + admToSats, + badParameter, + isAdmAddress, + isAdmPublicKey, + isAdmVoteForAddress, + isAdmVoteForDelegateName, + isAdmVoteForPublicKey, + isDelegateName, + isIntegerAmount, + isMessageType, + isPassphrase, + validateMessage, +} from '../helpers/validator'; +import {DEFAULT_GET_REQUEST_RETRIES, MessageType} from '../helpers/constants'; + +import type { + GetAccountBalanceResponseDto, + GetAccountInfoResponseDto, + GetAccountPublicKeyResponseDto, + GetAccountVotesResponseDto, + GetBlockInfoResponseDto, + GetBlocksResponseDto, + GetBroadhashResponseDto, + GetChatMessagesResponseDto, + GetChatRoomsResponseDto, + GetDelegateResponseDto, + GetDelegateStatsResponseDto, + GetDelegatesCountResponseDto, + GetDelegatesResponseDto, + GetEpochResponseDto, + GetHeightResponseDto, + GetLoadingStatusResponseDto, + GetMilestoneResponseDto, + GetNethashResponseDto, + GetNetworkInfoResponseDto, + GetNextForgersResponseDto, + GetNodeStatusResponseDto, + GetNodeVersionResponseDto, + GetPeersResponseDto, + GetPingStatusResponseDto, + GetQueuedTransactionsResponseDto, + GetRewardResponseDto, + GetSyncStatusResponseDto, + GetTokenTransferFeeResponseDto, + GetTokensTotalSupplyResponseDto, + GetTransactionByIdResponseDto, + GetTransactionTypesFeesResponseDto, + GetTransactionsCountResponseDto, + GetTransactionsResponseDto, + GetUnconfirmedTransactionByIdResponseDto, + GetUnconfirmedTransactionsResponseDto, + GetVotersResponseDto, + SearchDelegateResponseDto, +} from './generated'; +import { + createAddressFromPublicKey, + createKeypairFromPassphrase, +} from '../helpers/keys'; +import { + createChatTransaction, + createDelegateTransaction, + createSendTransaction, + createVoteTransaction, +} from '../helpers/transactions'; +import {encodeMessage} from '../helpers/encryptor'; +import {VoteDirection, parseVote} from './votes'; + +export type AdamantAddress = `U${string}`; + +export interface UsernameObject { + username: string; +} + +export interface PublicKeyObject { + publicKey: string; +} + +export interface AddressObject { + address: string; +} + +/** + * Object that contains either `address` or `publicKey` field + */ +export type AddressOrPublicKeyObject = AddressObject | PublicKeyObject; + +/** + * Object that contains either `username` or `publicKey` field + */ +export type UsernameOrPublicKeyObject = UsernameObject | PublicKeyObject; + +export type TransactionQuery = T & { + or: T; + and: T; +}; + +export interface GetBlocksOptions { + limit?: number; + offset?: number; + generatorPublicKey?: string; + height?: number; +} + +export interface GetDelegatesOptions { + limit?: number; + offset?: number; +} + +export interface TransactionQueryParameters { + blockId?: number; + fromHeight?: number; + toHeight?: number; + minAmount?: number; + maxAmount?: number; + senderId?: string; + recipientId?: string; + inId?: string; + limit?: number; + offset?: number; + orderBy?: string; +} + +// parameters that available for /api/chatrooms +export interface ChatroomsOptions extends TransactionQueryParameters { + type?: number; + withoutDirectTransfers?: boolean; +} + +// parameters that available for /api/transactions +export interface TransactionsOptions extends TransactionQueryParameters { + senderIds?: string[]; + recipientIds?: string[]; + senderPublicKey?: string; + senderPublicKeys?: string[]; + recipientPublicKey?: string; + recipientPublicKeys?: string[]; + type?: number; + types?: number[]; + returnAsset?: 1 | 0; +} + +export interface AdamantApiOptions extends NodeManagerOptions { + nodes: string[]; + + maxRetries?: number; + timeout?: number; + + logger?: CustomLogger; + logLevel?: LogLevel; +} + +const publicKeysCache: { + [address: string]: string; +} = {}; + +export class AdamantApi extends NodeManager { + maxRetries: number; + + constructor(options: AdamantApiOptions) { + const customLogger = new Logger(options.logLevel, options.logger); + + super(customLogger, options); + + this.maxRetries = options.maxRetries ?? DEFAULT_GET_REQUEST_RETRIES; + } + + private async request( + method: 'GET' | 'POST', + endpoint: string, + data: unknown, + retryNo = 1 + ): Promise> { + const {logger, maxRetries} = this; + + const url = `${this.node}/api/${endpoint}`; + + try { + const response = await axios>({ + method, + url, + ...(method === 'POST' + ? { + data, + } + : { + params: data, + }), + }); + + return response.data; + } catch (error) { + if (error instanceof AxiosError) { + const logMessage = `[ADAMANT js-api] Get-request: Request to ${url} failed with ${error + .response?.status} status code, ${error}${ + error.response?.data + ? '. Message: ' + error.response.data.toString().trim() + : '' + }. Try ${retryNo} of ${maxRetries}.`; + + if (retryNo <= maxRetries) { + logger.log(`${logMessage} Retrying…`); + + await this.updateNodes(); + return this.request(method, endpoint, data, retryNo + 1); + } + + logger.warn(`${logMessage} No more attempts, returning error.`); + + return { + success: false, + errorMessage: `${error}`, + }; + } + + return { + success: false, + errorMessage: `${error}`, + }; + } + } + + /** + * Makes GET request to ADAMANT network. + * + * @details `endpoint` should be in `'accounts/getPublicKey'` format, excluding `'/api/'` + */ + async get(endpoint: string, params?: unknown) { + return this.request('GET', endpoint, params); + } + + /** + * Makes POST request to ADAMANT network. + * + * @details `endpoint` should be in `'accounts/getPublicKey'` format, excluding `'/api/'` + */ + async post(endpoint: string, options: unknown) { + return this.request('POST', endpoint, options); + } + + /** + * Get account Public Key + */ + async getPublicKey(address: AdamantAddress) { + const cached = publicKeysCache[address]; + if (cached) { + return cached; + } + + const response = await this.get( + 'accounts/getPublicKey', + { + address, + } + ); + + if (response.success) { + const {publicKey} = response; + + publicKeysCache[address] = publicKey; + return publicKey; + } + + this.logger.warn( + `[ADAMANT js-api] Failed to get public key for ${address}. ${response.errorMessage}.` + ); + return ''; + } + + /** + * Register new delegate within given username + * + * @param username The new delegate's username + */ + async newDelegate(passphrase: string, username: string) { + if (!isPassphrase(passphrase)) { + return badParameter('passphrase'); + } + + if (!isDelegateName(username)) { + return badParameter('username'); + } + + const keyPair = createKeypairFromPassphrase(passphrase); + + const data = { + keyPair, + username, + }; + + const transaction = createDelegateTransaction(data); + + return this.post('delegates', transaction); + } + + /** + * Encrypt a message, creates Message transaction, signs it, and broadcasts to ADAMANT network. + */ + async sendMessage( + passphrase: string, + addressOrPublicKey: string, + message: string, + type = MessageType.Chat, + amount?: number, + isAmountInADM?: boolean + ) { + if (!isPassphrase(passphrase)) { + return badParameter('passphrase'); + } + + let address: AdamantAddress; + let publicKey = ''; + + if (!isAdmAddress(addressOrPublicKey)) { + if (!isAdmPublicKey(addressOrPublicKey)) { + return badParameter('addressOrPublicKey', addressOrPublicKey); + } + + publicKey = addressOrPublicKey; + + try { + address = createAddressFromPublicKey(publicKey); + } catch (error) { + return badParameter('addressOrPublicKey', addressOrPublicKey); + } + } else { + address = addressOrPublicKey; + } + + if (!isMessageType(type)) { + return badParameter('type', type); + } + + const result = validateMessage(message, type); + + if (!result.success) { + return badParameter('message', message, result.error); + } + + let amountInSat: number | undefined; + + if (amount) { + amountInSat = amount; + + if (isAmountInADM) { + amountInSat = admToSats(amount); + } + + if (!isIntegerAmount(amountInSat)) { + return badParameter('amount', amount); + } + } + + if (!publicKey) { + publicKey = await this.getPublicKey(address); + } + + if (!publicKey) { + return { + success: false, + errorMessage: `Unable to get public key for ${addressOrPublicKey}. It is necessary for sending an encrypted message. Account may be uninitialized (https://medium.com/adamant-im/chats-and-uninitialized-accounts-in-adamant-5035438e2fcd), or network error`, + }; + } + + const keyPair = createKeypairFromPassphrase(passphrase); + const encryptedMessage = encodeMessage(message, keyPair, publicKey); + + const data = { + ...encryptedMessage, + keyPair, + recipientId: address, + message_type: type, + amount: amountInSat, + }; + + const transaction = createChatTransaction(data); + + return this.post('transactions/process', { + transaction, + }); + } + + /** + * Send tokens to an account + */ + sendTokens( + passphrase: string, + addressOrPublicKey: string, + amount: number, + isAmountInADM = true + ) { + if (!isPassphrase(passphrase)) { + return badParameter('passphrase'); + } + + let address: AdamantAddress; + + if (isAdmAddress(addressOrPublicKey)) { + address = addressOrPublicKey; + } else { + if (!isAdmPublicKey(addressOrPublicKey)) { + return badParameter('addressOrPublicKey', addressOrPublicKey); + } + + try { + address = createAddressFromPublicKey(addressOrPublicKey); + } catch (error) { + return badParameter('addressOrPublicKey', addressOrPublicKey); + } + } + + let amountInSat = amount; + + if (isAmountInADM) { + amountInSat = admToSats(amount); + } + + if (!isIntegerAmount(amountInSat)) { + return badParameter('amount', amount); + } + + const keyPair = createKeypairFromPassphrase(passphrase); + + const data = { + keyPair, + recipientId: address, + amount: amountInSat, + }; + + const transaction = createSendTransaction(data); + + return this.post('transactions/process', { + transaction, + }); + } + + /** + * Vote for specific delegates + * + * @param votes Array with public keys. For upvote, add leading `+` to delegate's public key. For downvote, add leading `-` to delegate's public key. + * + * @example + * ``` + * voteForDelegate([ + * '+b3d0c0b99f64d0960324089eb678e90d8bcbb3dd8c73ee748e026f8b9a5b5468', + * '-9ef1f6212ae871716cfa2d04e3dc5339e8fe75f89818be21ee1d75004983e2a8' + * ]) + * ``` + */ + async voteForDelegate(passphrase: string, votes: string[]) { + if (!isPassphrase(passphrase)) { + return badParameter('passphrase'); + } + + const keyPair = createKeypairFromPassphrase(passphrase); + + const uniqueVotes: { + [publicKey: string]: VoteDirection; + } = {}; + + for (const vote of votes) { + const [name, direction] = parseVote(vote); + + const cachedPublicKey = publicKeysCache[name]; + + if (cachedPublicKey) { + uniqueVotes[cachedPublicKey] = direction; + continue; + } + + if (isAdmVoteForAddress(vote)) { + const response = await this.getAccountInfo({address: name}); + + if (!response.success) { + this.logger.warn( + `[ADAMANT js-api] Failed to get public key for ${vote}. ${response.errorMessage}.` + ); + return badParameter('votes'); + } + + const {publicKey} = response.account; + + publicKeysCache[name] = publicKey; + uniqueVotes[publicKey] = direction; + + continue; + } + + if (isAdmVoteForDelegateName(name)) { + const response = await this.getDelegate({username: name}); + + if (!response.success) { + this.logger.warn( + `[ADAMANT js-api] Failed to get public key for ${vote}. ${response.errorMessage}.` + ); + return badParameter('votes'); + } + + const {publicKey} = response.delegate; + + publicKeysCache[name] = publicKey; + uniqueVotes[publicKey] = direction; + + continue; + } + + if (!isAdmVoteForPublicKey(name)) { + return badParameter('votes'); + } + + uniqueVotes[name] = direction; + } + + const data = { + keyPair, + votes: Object.keys(uniqueVotes).map( + name => `${uniqueVotes[name]}${name}` + ), + }; + + const transaction = createVoteTransaction(data); + + return this.post('accounts/delegates', transaction); + } + + /** + * Get account information by ADAMANT address or Public Key + */ + async getAccountInfo( + options: AddressOrPublicKeyObject + ): Promise> { + return this.get('accounts', options); + } + + /** + * Get account balance + */ + async getAccountBalance(address: string) { + return this.get('accounts/getBalance', { + address, + }); + } + + /** + * Get block information by ID + */ + async getBlock(id: string) { + return this.get('blocks/get', {id}); + } + + /** + * Get list of blocks + */ + async getBlocks(options?: GetBlocksOptions) { + return this.get('blocks', options); + } + + /** + * Get list of Chats + */ + async getChats( + address: string, + options?: TransactionQuery + ) { + return this.get(`chatrooms/${address}`, options); + } + + /** + * Get messages between two accounts + */ + async getChatMessages( + address1: string, + address2: string, + query?: TransactionQuery + ) { + return this.get( + `chatrooms/${address1}/${address2}`, + query + ); + } + + /** + * Retrieves list of registered ADAMANT delegates + */ + async getDelegates(options: GetDelegatesOptions) { + return this.get('delegates', options); + } + + /** + * Get delegate info by `username` or `publicKey` + */ + async getDelegate(options: UsernameOrPublicKeyObject) { + return this.get('delegates/get', options); + } + + /** + * Search delegates by `username` + * + * @param q - username + */ + async searchDelegates(q: string) { + return this.get('delegates/search', {q}); + } + + /** + * Get total count of delegates + */ + async getDelegatesCount() { + return this.get('delegates/count'); + } + + /** + * Get forging activity of a delegate + */ + async getDelegateStats(generatorPublicKey: string) { + return this.get( + 'delegates/forging/getForgedByAccount', + {generatorPublicKey} + ); + } + + /** + * Returns list of next forgers + * + * @param limit count to retrieve + */ + async getNextForgers(limit?: number) { + return this.get('delegates/getNextForgers', { + limit, + }); + } + + /** + * Gets list of delegate's voters + * + * @param publicKey representing delegate's publicKey + */ + async getVoters(publicKey: string) { + return this.get('delegates/voters', {publicKey}); + } + + /** + * Gets current votes of specific ADAMANT account + * + * @param address address of the account to get votes + */ + async getVoteData(address: string) { + return this.get('accounts/delegates', { + address, + }); + } + + /** + * Gets list of connected peer nodes + */ + async getPeers() { + return this.get('peers'); + } + + /** + * Gets loading status + */ + async getLoadingStatus() { + return this.get('loader/status'); + } + + /** + * Gets information on node's sync process with other peers + */ + async getSyncStatus() { + return this.get('loader/status/sync'); + } + + /** + * Checks if the connected node is alive + */ + async getPingStatus() { + return this.get('loader/status/ping'); + } + + /** + * Gets node's software information + */ + async getNodeVersion() { + return this.get('peers/version'); + } + + /** + * Broadhash is established as an aggregated rolling hash of the past five blocks present in the database. + */ + async getBroadhash() { + return this.get('blocks/getBroadhash'); + } + + /** + * Returns time when blockchain epoch starts. Value `2017-09-02T17:00:00.000Z` is for mainnet. + */ + async getEpoch() { + return this.get('blocks/getEpoch'); + } + + /** + * Returns current node's blockchain height + */ + async getHeight() { + return this.get('blocks/getHeight'); + } + + /** + * Returns current fee value for `type 0` (token transfer) transactions. + * Integer amount of 1/10^8 ADM tokens (1 ADM = 100000000). + */ + async getFee() { + return this.get('blocks/getFee'); + } + + /** + * Returns current fee values for different transaction types + */ + async getFees() { + return this.get('blocks/getFees'); + } + + /** + * The nethash describes e.g. the Mainnet or the Testnet, that the node is connecting to. + */ + async getNethash() { + return this.get('blocks/getNethash'); + } + + /** + * Return current slot height, which determines reward a delegate will get for forging a block. + */ + async getMilestone() { + return this.get('blocks/getMilestone'); + } + + /** + * Returns reward — the reward a delegate will get for forging a block. + * Integer amount of 1/10^8 ADM tokens (1 ADM = 100000000). Depends on the slot height. + */ + async getReward() { + return this.get('blocks/getReward'); + } + + /** + * Returns total current supply of ADM tokens in network. Integer amount of 1/10^8 ADM tokens (1 ADM = 100000000). + * Total supply increases with every new forged block. + */ + async getSupply() { + return this.get('blocks/getSupply'); + } + + /** + * Returns blockchain network information in a single request + */ + async getStatus() { + return this.get('blocks/getStatus'); + } + + /** + * Returns both ADAMANT blockchain network information and Node information in a single request. + */ + async getNodeStatus() { + return this.get('node/status'); + } + + /** + * Returns list of transactions + */ + async getTransactions(options?: TransactionQuery) { + return this.get('transactions', options); + } + + /** + * Get transaction by ID + */ + async getTransaction( + id: number, + options?: TransactionQuery + ) { + return this.get('transactions/get', { + id, + ...options, + }); + } + + /** + * Get `confirmed`, `uncofirmed` and `queued` transactions count + * + * @nav Transactions + */ + async getTransactionsCount() { + return this.get('transactions/count'); + } + + /** + * Get queued transactions count + */ + async getQueuedTransactions() { + return this.get('transactions/queued'); + } + + /** + * Get queued transaction by ID + */ + async getQueuedTransaction(id: number) { + return this.get( + 'transactions/queued/get', + {id} + ); + } + + /** + * Get unconfirmed transactions + */ + async getUnconfirmedTransactions() { + return this.get( + 'transactions/unconfirmed' + ); + } + + /** + * Get unconfirmed transaction by ID + */ + async getUnconfirmedTransaction(id: number) { + return this.get( + 'transactions/unconfirmed/get', + {id} + ); + } +} + +export * from './generated'; +export * from './votes'; diff --git a/src/api/votes.ts b/src/api/votes.ts new file mode 100644 index 0000000..7e00af1 --- /dev/null +++ b/src/api/votes.ts @@ -0,0 +1,8 @@ +export type VoteDirection = '+' | '-'; + +export const parseVote = (vote: string): [string, VoteDirection] => { + const name = vote.slice(1); + const direction = vote.charAt(0); + + return [name, direction as VoteDirection]; +}; diff --git a/src/coins/btc.ts b/src/coins/btc.ts new file mode 100644 index 0000000..15dbb5b --- /dev/null +++ b/src/coins/btc.ts @@ -0,0 +1,31 @@ +import * as bitcoin from 'bitcoinjs-lib'; +import {ECPairFactory} from 'ecpair'; +import * as tinysecp from 'tiny-secp256k1'; + +import coininfo from 'coininfo'; + +const RE_BTC_ADDRESS = /^(bc1|[13])[a-km-zA-HJ-NP-Z02-9]{25,39}$/; + +const network = coininfo.bitcoin.main.toBitcoinJS(); + +export const btc = { + keys: (passphrase: string) => { + const pwHash = bitcoin.crypto.sha256(Buffer.from(passphrase)); + + const ECPairAPI = ECPairFactory(tinysecp); + const keyPair = ECPairAPI.fromPrivateKey(pwHash, {network}); + + return { + network, + keyPair, + address: bitcoin.payments.p2pkh({pubkey: keyPair.publicKey, network}) + .address, + // BTC private key is a regular 256-bit key + privateKey: keyPair.privateKey?.toString('hex'), // regular 256-bit (32 bytes, 64 characters) private key + privateKeyWIF: keyPair.toWIF(), // Wallet Import Format (52 base58 characters) + }; + }, + + isValidAddress: (address: string) => + typeof address === 'string' && RE_BTC_ADDRESS.test(address), +}; diff --git a/src/coins/dash.ts b/src/coins/dash.ts new file mode 100644 index 0000000..3c6b47d --- /dev/null +++ b/src/coins/dash.ts @@ -0,0 +1,30 @@ +import * as bitcoin from 'bitcoinjs-lib'; +import {ECPairFactory} from 'ecpair'; +import * as tinysecp from 'tiny-secp256k1'; + +import coininfo from 'coininfo'; + +const RE_DASH_ADDRESS = /^[7X][1-9A-HJ-NP-Za-km-z]{33,}$/; + +const network = coininfo.dash.main.toBitcoinJS(); + +export const dash = { + keys: (passphrase: string) => { + const pwHash = bitcoin.crypto.sha256(Buffer.from(passphrase)); + + const ECPairAPI = ECPairFactory(tinysecp); + const keyPair = ECPairAPI.fromPrivateKey(pwHash, {network}); + + return { + network, + keyPair, + address: bitcoin.payments.p2pkh({pubkey: keyPair.publicKey, network}) + .address, + // DASH private key is a regular 256-bit key + privateKey: keyPair.privateKey?.toString('hex'), // regular 256-bit (32 bytes, 64 characters) private key + privateKeyWIF: keyPair.toWIF(), // Wallet Import Format (52 base58 characters) + }; + }, + isValidAddress: (address: string) => + typeof address === 'string' && RE_DASH_ADDRESS.test(address), +}; diff --git a/src/coins/doge.ts b/src/coins/doge.ts new file mode 100644 index 0000000..a7045b0 --- /dev/null +++ b/src/coins/doge.ts @@ -0,0 +1,30 @@ +import * as bitcoin from 'bitcoinjs-lib'; +import {ECPairFactory} from 'ecpair'; +import * as tinysecp from 'tiny-secp256k1'; + +import coininfo from 'coininfo'; + +const RE_DOGE_ADDRESS = /^[A|D|9][A-Z0-9]([0-9a-zA-Z]{9,})$/; + +const network = coininfo.dogecoin.main.toBitcoinJS(); + +export const doge = { + keys: (passphrase: string) => { + const pwHash = bitcoin.crypto.sha256(Buffer.from(passphrase)); + + const ECPairAPI = ECPairFactory(tinysecp); + const keyPair = ECPairAPI.fromPrivateKey(pwHash, {network}); + + return { + network, + keyPair, + address: bitcoin.payments.p2pkh({pubkey: keyPair.publicKey, network}) + .address, + // DOGE private key is a regular 256-bit key + privateKey: keyPair.privateKey?.toString('hex'), // regular 256-bit (32 bytes, 64 characters) private key + privateKeyWIF: keyPair.toWIF(), // Wallet Import Format (52 base58 characters) + }; + }, + isValidAddress: (address: string) => + typeof address === 'string' && RE_DOGE_ADDRESS.test(address), +}; diff --git a/src/coins/eth.ts b/src/coins/eth.ts new file mode 100644 index 0000000..75fefa4 --- /dev/null +++ b/src/coins/eth.ts @@ -0,0 +1,19 @@ +import {mnemonicToSeedSync} from 'bip39'; +import hdkey from 'hdkey'; +import {bufferToHex, privateToAddress} from 'ethereumjs-util'; + +const HD_KEY_PATH = "m/44'/60'/3'/1/0"; + +export const eth = { + keys: (passphrase: string) => { + const seed = mnemonicToSeedSync(passphrase); + const privateKey = hdkey + .fromMasterSeed(seed) + .derive(HD_KEY_PATH).privateKey; + + return { + address: bufferToHex(privateToAddress(privateKey)), + privateKey: bufferToHex(privateKey), + }; + }, +}; diff --git a/src/coins/index.ts b/src/coins/index.ts new file mode 100644 index 0000000..4e63994 --- /dev/null +++ b/src/coins/index.ts @@ -0,0 +1,5 @@ +export * from './btc'; +export * from './dash'; +export * from './doge'; +export * from './eth'; +export * from './lsk'; diff --git a/src/coins/lsk.ts b/src/coins/lsk.ts new file mode 100644 index 0000000..a52df93 --- /dev/null +++ b/src/coins/lsk.ts @@ -0,0 +1,48 @@ +import * as cryptography from '@liskhq/lisk-cryptography'; +import sodium from 'sodium-browserify-tweetnacl'; +import pbkdf2 from 'pbkdf2'; +import {bytesToHex} from '../helpers/encryptor'; + +const LiskHashSettings = { + SALT: 'adm', + ITERATIONS: 2048, + KEYLEN: 32, + DIGEST: 'sha256', +}; + +const network = { + name: 'Lisk', + port: 8000, + wsPort: 8001, + unit: 'LSK', +}; + +export const lsk = { + keys: (passphrase: string) => { + const liskSeed = pbkdf2.pbkdf2Sync( + passphrase, + LiskHashSettings.SALT, + LiskHashSettings.ITERATIONS, + LiskHashSettings.KEYLEN, + LiskHashSettings.DIGEST + ); + const keyPair = sodium.crypto_sign_seed_keypair(liskSeed); + const address = cryptography.getBase32AddressFromPublicKey( + keyPair.publicKey + ); + const addressHexBinary = cryptography.getAddressFromPublicKey( + keyPair.publicKey + ); + const addressHex = bytesToHex(addressHexBinary); + const privateKey = keyPair.secretKey.toString('hex'); + + return { + network, + keyPair, + address, + addressHexBinary, + addressHex, + privateKey, + }; + }, +}; diff --git a/src/coins/tests/btc.test.ts b/src/coins/tests/btc.test.ts new file mode 100644 index 0000000..14736d9 --- /dev/null +++ b/src/coins/tests/btc.test.ts @@ -0,0 +1,19 @@ +import {btc} from '../btc'; +import {passphrase} from './mock/passphrase'; + +describe('btc.keys()', () => { + test('should create keys with bitcoin network and address/privateKey for the passphrase', () => { + const keys = btc.keys(passphrase); + + expect(keys).toMatchObject({ + address: '13rK42XbSJV9BdvKQvDJeH3n45zNBbXsUV', + privateKey: + '0c9c84722d74ae5c5ba52f74285807ef08085a66aa7b23377fa8cdd51f1ecff3', + privateKeyWIF: 'KweE3oRFhusodiwEsXjEH9rBR1HUfkZsHjHZHPzvKtXRjroArofw', + network: { + name: 'Bitcoin', + unit: 'BTC', + }, + }); + }); +}); diff --git a/src/coins/tests/dash.test.ts b/src/coins/tests/dash.test.ts new file mode 100644 index 0000000..26b81e3 --- /dev/null +++ b/src/coins/tests/dash.test.ts @@ -0,0 +1,19 @@ +import {dash} from '../dash'; +import {passphrase} from './mock/passphrase'; + +describe('dash.keys()', () => { + test('should create keys with dash network and address/privateKey for the passphrase', () => { + const keys = dash.keys(passphrase); + + expect(keys).toMatchObject({ + address: 'XdY9tHBVQ1hjLaWuGoXXVojZtRa4GfEdNP', + privateKey: + '0c9c84722d74ae5c5ba52f74285807ef08085a66aa7b23377fa8cdd51f1ecff3', + privateKeyWIF: 'XBi9W4od1bWFh3wcuHj6nP3CL2Z47ZB7fJdTovL7eFoX91tC1QD4', + network: { + name: 'Dash', + unit: 'DASH', + }, + }); + }); +}); diff --git a/src/coins/tests/doge.test.ts b/src/coins/tests/doge.test.ts new file mode 100644 index 0000000..445e353 --- /dev/null +++ b/src/coins/tests/doge.test.ts @@ -0,0 +1,19 @@ +import {doge} from '../doge'; +import {passphrase} from './mock/passphrase'; + +describe('doge.keys()', () => { + test('should create keys with doge network and address/privateKey for the passphrase', () => { + const keys = doge.keys(passphrase); + + expect(keys).toMatchObject({ + address: 'D7zQbHUEjiPRie6v9WCsC3DNwDifUdbFdd', + privateKey: + '0c9c84722d74ae5c5ba52f74285807ef08085a66aa7b23377fa8cdd51f1ecff3', + privateKeyWIF: 'QP39CeEExXMvsFLHToa2ANgnt3K3iJhgAyyU52Pm4F8nBniweSFq', + network: { + name: 'Dogecoin', + unit: 'DOGE', + }, + }); + }); +}); diff --git a/src/coins/tests/eth.test.ts b/src/coins/tests/eth.test.ts new file mode 100644 index 0000000..663bcc6 --- /dev/null +++ b/src/coins/tests/eth.test.ts @@ -0,0 +1,14 @@ +import {eth} from '../eth'; +import {passphrase} from './mock/passphrase'; + +describe('eth.keys()', () => { + test('should create address and privateKey for the passphrase', () => { + const keys = eth.keys(passphrase); + + expect(keys).toMatchObject({ + address: '0x6c892b27f6deb1c81ed0122b23193c5802464c2c', + privateKey: + '0x2a3d59f8cab3c8b90d2e841c85872d0a78c75ac8d0b74186f856b3e954121277', + }); + }); +}); diff --git a/src/coins/tests/lsk.test.ts b/src/coins/tests/lsk.test.ts new file mode 100644 index 0000000..b2ddca1 --- /dev/null +++ b/src/coins/tests/lsk.test.ts @@ -0,0 +1,19 @@ +import {lsk} from '../lsk'; +import {passphrase} from './mock/passphrase'; + +describe('eth.keys()', () => { + test('should create address and privateKey for the passphrase', () => { + const keys = lsk.keys(passphrase); + + expect(keys).toMatchObject({ + address: 'lsk53o76hxv9kkc4cq7239gbgtgjhq6uswgt5aqxc', + addressHex: '5a18e574226d28348eaec21bf37e7fe76aa86eff', + privateKey: + 'b0ec632b3ceb31dd2f1cfa8d3c16086f0d14c1344a126d0136bca3bf00d4fc1dddf501ed87c5d950241622ca678a7a96f59e63aa1a0ef89d4a6bbf096ba00471', + network: { + name: 'Lisk', + unit: 'LSK', + }, + }); + }); +}); diff --git a/src/coins/tests/mock/passphrase.ts b/src/coins/tests/mock/passphrase.ts new file mode 100644 index 0000000..657b41d --- /dev/null +++ b/src/coins/tests/mock/passphrase.ts @@ -0,0 +1,2 @@ +export const passphrase = + 'learn arch equip tenant cause can brief brisk rich betray arrest damage'; diff --git a/src/groups/btc.js b/src/groups/btc.js deleted file mode 100644 index 58f1ebd..0000000 --- a/src/groups/btc.js +++ /dev/null @@ -1,30 +0,0 @@ -const bitcoin = require('bitcoinjs-lib'); -const {ECPairFactory} = require('ecpair'); -const tinysecp = require('tiny-secp256k1'); - -const coinNetworks = require('./coinNetworks'); -const btc = {}; - -/** - * Generates a BTC account from the passphrase specified. - * @param {string} passphrase ADAMANT account passphrase - * @return {object} network info, keyPair, privateKey, privateKeyWIF - */ -btc.keys = (passphrase) => { - const network = coinNetworks.BTC; - const pwHash = bitcoin.crypto.sha256(Buffer.from(passphrase)); - - const ECPairAPI = new ECPairFactory(tinysecp); - const keyPair = ECPairAPI.fromPrivateKey(pwHash, {network}); - - return { - network, - keyPair, - address: bitcoin.payments.p2pkh({pubkey: keyPair.publicKey, network}).address, - // BTC private key is a regular 256-bit key - privateKey: keyPair.privateKey.toString('hex'), // regular 256-bit (32 bytes, 64 characters) private key - privateKeyWIF: keyPair.toWIF(), // Wallet Import Format (52 base58 characters) - }; -}; - -module.exports = btc; diff --git a/src/groups/coinNetworks.js b/src/groups/coinNetworks.js deleted file mode 100644 index d3bc749..0000000 --- a/src/groups/coinNetworks.js +++ /dev/null @@ -1,13 +0,0 @@ -const coininfo = require('coininfo'); - -module.exports = { - DOGE: coininfo.dogecoin.main.toBitcoinJS(), - DASH: coininfo.dash.main.toBitcoinJS(), - BTC: coininfo.bitcoin.main.toBitcoinJS(), - LSK: { - name: 'Lisk', - port: 8000, - wsPort: 8001, - unit: 'LSK', - }, -}; diff --git a/src/groups/dash.js b/src/groups/dash.js deleted file mode 100644 index 66ce2d9..0000000 --- a/src/groups/dash.js +++ /dev/null @@ -1,30 +0,0 @@ -const bitcoin = require('bitcoinjs-lib'); -const {ECPairFactory} = require('ecpair'); -const tinysecp = require('tiny-secp256k1'); - -const coinNetworks = require('./coinNetworks'); -const dash = { }; - -/** - * Generates a DASH account from the passphrase specified. - * @param {string} passphrase ADAMANT account passphrase - * @return {object} network info, keyPair, privateKey, privateKeyWIF - */ -dash.keys = (passphrase) => { - const network = coinNetworks.DASH; - const pwHash = bitcoin.crypto.sha256(Buffer.from(passphrase)); - - const ECPairAPI = new ECPairFactory(tinysecp); - const keyPair = ECPairAPI.fromPrivateKey(pwHash, {network}); - - return { - network, - keyPair, - address: bitcoin.payments.p2pkh({pubkey: keyPair.publicKey, network}).address, - // DASH private key is a regular 256-bit key - privateKey: keyPair.privateKey.toString('hex'), // regular 256-bit (32 bytes, 64 characters) private key - privateKeyWIF: keyPair.toWIF(), // Wallet Import Format (52 base58 characters) - }; -}; - -module.exports = dash; diff --git a/src/groups/decodeMsg.js b/src/groups/decodeMsg.js deleted file mode 100644 index 3b700c1..0000000 --- a/src/groups/decodeMsg.js +++ /dev/null @@ -1,83 +0,0 @@ -const ed2curve = require('ed2curve'); -const nacl = require('tweetnacl/nacl-fast'); -const keys = require('../helpers/keys'); - -module.exports = (msg, senderPublicKey, passPhrase, nonce) => { - const keypair = keys.createKeypairFromPassPhrase(passPhrase); - - let {privateKey} = keypair; - - if (typeof msg === 'string') { - msg = hexToBytes(msg); - } - - if (typeof nonce === 'string') { - nonce = hexToBytes(nonce); - } - - if (typeof senderPublicKey === 'string') { - senderPublicKey = hexToBytes(senderPublicKey); - } - - if (typeof privateKey === 'string') { - privateKey = hexToBytes(privateKey); - } - - const DHPublicKey = ed2curve.convertPublicKey(senderPublicKey); - const DHSecretKey = ed2curve.convertSecretKey(privateKey); - const decrypted = nacl.box.open(msg, nonce, DHPublicKey, DHSecretKey); - - return decrypted ? utf8ArrayToStr(decrypted) : ''; -}; - -function hexToBytes(hexString = '') { - const bytes = []; - - for (let c = 0; c < hexString.length; c += 2) { - bytes.push(parseInt(hexString.substring(c, c + 2), 16)); - } - - return Uint8Array.from(bytes); -} - -function utf8ArrayToStr(array) { - const len = array.length; - let out = ''; - let i = 0; - let c; - let char2; - let char3; - - while (i < len) { - c = array[i++]; - switch (c >> 4) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - // 0xxxxxxx - out += String.fromCharCode(c); - break; - case 12: - case 13: - // 110x xxxx 10xx xxxx - char2 = array[i++]; - out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); - break; - case 14: - // 1110 xxxx 10xx xxxx 10xx xxxx - char2 = array[i++]; - char3 = array[i++]; - out += String.fromCharCode(((c & 0x0F) << 12) | - ((char2 & 0x3F) << 6) | - ((char3 & 0x3F) << 0)); - break; - } - } - - return out; -} diff --git a/src/groups/doge.js b/src/groups/doge.js deleted file mode 100644 index eaf87d9..0000000 --- a/src/groups/doge.js +++ /dev/null @@ -1,30 +0,0 @@ -const bitcoin = require('bitcoinjs-lib'); -const {ECPairFactory} = require('ecpair'); -const tinysecp = require('tiny-secp256k1'); - -const coinNetworks = require('./coinNetworks'); -const doge = {}; - -/** - * Generates a DOGE account from the passphrase specified. - * @param {string} passphrase ADAMANT account passphrase - * @return {object} network info, keyPair, privateKey, privateKeyWIF - */ -doge.keys = (passphrase) => { - const network = coinNetworks.DOGE; - const pwHash = bitcoin.crypto.sha256(Buffer.from(passphrase)); - - const ECPairAPI = new ECPairFactory(tinysecp); - const keyPair = ECPairAPI.fromPrivateKey(pwHash, {network}); - - return { - network, - keyPair, - address: bitcoin.payments.p2pkh({pubkey: keyPair.publicKey, network}).address, - // DOGE private key is a regular 256-bit key - privateKey: keyPair.privateKey.toString('hex'), // regular 256-bit (32 bytes, 64 characters) private key - privateKeyWIF: keyPair.toWIF(), // Wallet Import Format (52 base58 characters) - }; -}; - -module.exports = doge; diff --git a/src/groups/eth.js b/src/groups/eth.js deleted file mode 100644 index 524f43e..0000000 --- a/src/groups/eth.js +++ /dev/null @@ -1,24 +0,0 @@ -const Mnemonic = require('bitcore-mnemonic'); -const hdkey = require('hdkey'); -const HD_KEY_PATH = 'm/44\'/60\'/3\'/1/0'; -const {bufferToHex, privateToAddress} = require('ethereumjs-util'); -const eth = { }; - -/** - * Generates a ETH account from the passphrase specified. - * @param {string} passphrase ADAMANT account passphrase - * @returns {{address: String, privateKey: Buffer}} - */ - -eth.keys = (passphrase) => { - const mnemonic = new Mnemonic(passphrase, Mnemonic.Words.ENGLISH); - const seed = mnemonic.toSeed(); - const privateKey = hdkey.fromMasterSeed(seed).derive(HD_KEY_PATH)._privateKey; - - return { - address: bufferToHex(privateToAddress(privateKey)), - privateKey: bufferToHex(privateKey), - }; -}; - -module.exports = eth; diff --git a/src/groups/get.js b/src/groups/get.js deleted file mode 100644 index 8090efa..0000000 --- a/src/groups/get.js +++ /dev/null @@ -1,48 +0,0 @@ -const axios = require('../helpers/axiosClient'); -const logger = require('../helpers/logger'); -const validator = require('../helpers/validator'); - -const DEFAULT_GET_REQUEST_RETRIES = 3; // How much re-tries for get-requests by default. Total 3+1 tries - -module.exports = (nodeManager) => { - return (endpoint, params, maxRetries = DEFAULT_GET_REQUEST_RETRIES, retryNo = 0) => { - let url = trimAny(endpoint, '/ ').replace(/^api\//, ''); - if (!url || !validator.validateEndpoint(endpoint)) { - return validator.badParameter('endpoint'); - } - - url = nodeManager.node() + '/api/' + url; - return axios.get(url, {params}) - .then(function(response) { - return validator.formatRequestResults(response, true); - }) - .catch(function(error) { - const logMessage = `[ADAMANT js-api] Get-request: Request to ${url} failed with ${error.response ? error.response.status : undefined} status code, ${error.toString()}${error.response && error.response.data ? '. Message: ' + error.response.data.toString().trim() : ''}. Try ${retryNo + 1} of ${maxRetries + 1}.`; - if (retryNo < maxRetries) { - logger.log(`${logMessage} Retrying…`); - return nodeManager.changeNodes() - .then(function() { - return module.exports(nodeManager)(endpoint, params, maxRetries, ++retryNo); - }); - } - logger.warn(`${logMessage} No more attempts, returning error.`); - return validator.formatRequestResults(error, false); - }); - }; -}; - -function trimAny(str, chars) { - if (!str || typeof str !== 'string') { - return ''; - } - let start = 0; - let end = str.length; - while (start < end && chars.indexOf(str[start]) >= 0) { - ++start; - } - while (end > start && chars.indexOf(str[end - 1]) >= 0) { - --end; - } - return (start > 0 || end < str.length) ? str.substring(start, end) : str; -} - diff --git a/src/groups/getPublicKey.js b/src/groups/getPublicKey.js deleted file mode 100644 index 1ce213f..0000000 --- a/src/groups/getPublicKey.js +++ /dev/null @@ -1,20 +0,0 @@ -const get = require('./get'); -const logger = require('../helpers/logger'); -const publicKeysCache = { }; - -module.exports = (nodeManager) => { - return async (address) => { - if (publicKeysCache[address]) { - return publicKeysCache[address]; - } - - const publicKey = await get(nodeManager)('/accounts/getPublicKey', {address}); - if (publicKey.success) { - publicKeysCache[address] = publicKey.data.publicKey; - return publicKey.data.publicKey; - } else { - logger.warn(`[ADAMANT js-api] Failed to get public key for ${address}. ${publicKey.errorMessage}.`); - return false; - } - }; -}; diff --git a/src/groups/lsk.js b/src/groups/lsk.js deleted file mode 100644 index 9d2ea53..0000000 --- a/src/groups/lsk.js +++ /dev/null @@ -1,42 +0,0 @@ -const cryptography = require('@liskhq/lisk-cryptography'); -const sodium = require('sodium-browserify-tweetnacl'); -const pbkdf2 = require('pbkdf2'); - -const coinNetworks = require('./coinNetworks'); -const {bytesToHex} = require('../helpers/encryptor'); - -const lsk = {}; - -const LiskHashSettings = { - SALT: 'adm', - ITERATIONS: 2048, - KEYLEN: 32, - DIGEST: 'sha256', -}; - -/** - * Generates a LSK account from the passphrase specified. - * @param {string} passphrase ADAMANT account passphrase - * @returns {object} network info, keyPair, address, addressHexBinary, addressHex, privateKey - */ - -lsk.keys = (passphrase) => { - const network = coinNetworks.LSK; - const liskSeed = pbkdf2.pbkdf2Sync(passphrase, LiskHashSettings.SALT, LiskHashSettings.ITERATIONS, LiskHashSettings.KEYLEN, LiskHashSettings.DIGEST); - const keyPair = sodium.crypto_sign_seed_keypair(liskSeed); - const address = cryptography.getBase32AddressFromPublicKey(keyPair.publicKey); - const addressHexBinary = cryptography.getAddressFromPublicKey(keyPair.publicKey); - const addressHex = bytesToHex(addressHexBinary); - const privateKey = keyPair.secretKey.toString('hex'); - - return { - network, - keyPair, - address, - addressHexBinary, - addressHex, - privateKey, - }; -}; - -module.exports = lsk; diff --git a/src/groups/newDelegate.js b/src/groups/newDelegate.js deleted file mode 100644 index 48088b2..0000000 --- a/src/groups/newDelegate.js +++ /dev/null @@ -1,71 +0,0 @@ -const axios = require('../helpers/axiosClient'); -const logger = require('../helpers/logger'); -const keys = require('../helpers/keys'); -const constants = require('../helpers/constants'); -const transactionFormer = require('../helpers/transactionFormer'); -const validator = require('../helpers/validator'); - -const DEFAULT_NEW_DELEGATE_RETRIES = 4; // How much re-tries for send tokens requests by default. Total 4+1 tries - -module.exports = (nodeManager) => { - /** - * Registers user account as delegate - * @param {string} passPhrase Senders's passPhrase. Sender's address will be derived from it. - * @param {string} username Delegate name you want to register with. - * It must be unique in ADAMANT blockchain. It should not be similar to ADAMANT address. - * Delegate name can only contain alphanumeric characters and symbols !@$&_. - * @param {number} maxRetries How much times to retry request - * @param {number} retryNo Number of request already made - * @return {Promise} Request results - */ - return async (passPhrase, username, maxRetries = DEFAULT_NEW_DELEGATE_RETRIES, retryNo = 0) => { - let transaction; - - try { - if (!validator.validatePassPhrase(passPhrase)) { - return validator.badParameter('passPhrase'); - } - - const keyPair = keys.createKeypairFromPassPhrase(passPhrase); - - if (!validator.validateDelegateName(username)) { - return validator.badParameter('username'); - } - - const type = constants.transactionTypes.DELEGATE; - - const data = { - type, - keyPair, - username, - }; - - transaction = transactionFormer.createTransaction(type, data); - } catch (e) { - return validator.badParameter('#exception_catched#', e); - } - - const url = nodeManager.node() + '/api/delegates'; - - try { - const response = await axios.post(url, transaction); - - return validator.formatRequestResults(response, true); - } catch (error) { - const logMessage = `[ADAMANT js-api] New delegate request: Request to ${url} failed with ${error.response ? error.response.status : undefined} status code, ${error.toString()}${error.response && error.response.data ? '. Message: ' + error.response.data.toString().trim() : ''}. Try ${retryNo+1} of ${maxRetries+1}.`; - - if (retryNo < maxRetries) { - logger.log(`${logMessage} Retrying…`); - - return nodeManager.changeNodes() - .then(() => ( - module.exports(nodeManager)(passPhrase, username, maxRetries, ++retryNo) - )); - } - - logger.warn(`${logMessage} No more attempts, returning error.`); - - return validator.formatRequestResults(error, false); - } - }; -}; diff --git a/src/groups/sendMessage.js b/src/groups/sendMessage.js deleted file mode 100644 index e402f34..0000000 --- a/src/groups/sendMessage.js +++ /dev/null @@ -1,142 +0,0 @@ -const axios = require('../helpers/axiosClient'); -const logger = require('../helpers/logger'); -const keys = require('../helpers/keys'); -const constants = require('../helpers/constants'); -const encryptor = require('../helpers/encryptor'); -const transactionFormer = require('../helpers/transactionFormer'); -const validator = require('../helpers/validator'); -const getPublicKey = require('./getPublicKey'); - -const DEFAULT_SEND_MESSAGE_RETRIES = 4; // How much re-tries for send message requests by default. Total 4+1 tries - -module.exports = (nodeManager) => { - /** - * Encrypts a message, creates Message transaction, signs it, and broadcasts to ADAMANT network. Supports Basic, Rich and Signal Message Types. - * See https://github.com/Adamant-im/adamant/wiki/Message-Types - * @param {string} passPhrase Senders's passPhrase. Sender's address will be derived from it. - * @param {string} addressOrPublicKey Recipient's ADAMANT address or public key. - * Using public key is faster, as the library wouldn't request it from the network. - * Though we cache public keys, and next request with address will be processed as fast as with public key. - * @param {string} message Message plain text in case of basic message. Stringified JSON in case of rich or signal messages. The library will encrypt a message. - * Example of rich message for Ether in-chat transfer: - * `{"type":"eth_transaction","amount":"0.002","hash":"0xfa46d2b3c99878f1f9863fcbdb0bc27d220d7065c6528543cbb83ced84487deb","comments":"I like to send it, send it"}` - * @param {string | number} messageType Type of message: basic, rich, or signal - * @param {string | number} amount Amount to send with a message - * @param {boolean} isAmountInADM If amount specified in ADM, or in sats (10^-8 ADM) - * @param {number} maxRetries How much times to retry request - * @param {number} retryNo Number of request already made - * @return {Promise} Request results - */ - return async (passPhrase, addressOrPublicKey, message, messageType = 'basic', amount, isAmountInADM = true, maxRetries = DEFAULT_SEND_MESSAGE_RETRIES, retryNo = 0) => { - let keyPair; - let data; - let address; - let publicKey; - - try { - if (!validator.validatePassPhrase(passPhrase)) { - return validator.badParameter('passPhrase'); - } - - keyPair = keys.createKeypairFromPassPhrase(passPhrase); - - if (!validator.validateAdmAddress(addressOrPublicKey)) { - if (!validator.validateAdmPublicKey(addressOrPublicKey)) { - return validator.badParameter('addressOrPublicKey', addressOrPublicKey); - } else { - publicKey = addressOrPublicKey; - try { - address = keys.createAddressFromPublicKey(publicKey); - } catch (e) { - return validator.badParameter('addressOrPublicKey', addressOrPublicKey); - } - } - } else { - publicKey = ''; - address = addressOrPublicKey; - } - - const messageTypes = { - basic: 1, - rich: 2, - signal: 3, - }; - - messageType = messageTypes[messageType]; - - if (!validator.validateMessageType(messageType)) { - return validator.badParameter('messageType', messageType); - } - - const messageValidation = validator.validateMessage(message, messageType); - - if (!messageValidation.result) { - return validator.badParameter('message', message, messageValidation.error); - } - - data = { - keyPair, - recipientId: address, - message_type: messageType, - }; - - if (amount) { - let amountInSat = amount; - - if (isAmountInADM) { - amountInSat = validator.admToSats(amount); - } - - if (!validator.validateIntegerAmount(amountInSat)) { - return validator.badParameter('amount', amount); - } - - data.amount = amountInSat; - } - } catch (e) { - return validator.badParameter('#exception_catched#', e); - } - - if (!publicKey) { - publicKey = await getPublicKey(nodeManager)(address); - } - - if (!publicKey) { - return { - success: false, - errorMessage: `Unable to get public key for ${addressOrPublicKey}. It is necessary for sending an encrypted message. Account may be uninitialized (https://medium.com/adamant-im/chats-and-uninitialized-accounts-in-adamant-5035438e2fcd), or network error`, - }; - } - - try { - const encryptedMessage = encryptor.encodeMessage(message, keyPair, publicKey); - data.message = encryptedMessage.message; - data.own_message = encryptedMessage.own_message; - - const transaction = transactionFormer.createTransaction(constants.transactionTypes.CHAT_MESSAGE, data); - - const url = nodeManager.node() + '/api/transactions/process'; - return axios.post(url, {transaction}) - .then(function(response) { - return validator.formatRequestResults(response, true); - }) - .catch(function(error) { - const logMessage = `[ADAMANT js-api] Send message request: Request to ${url} failed with ${error.response ? error.response.status : undefined} status code, ${error.toString()}${error.response && error.response.data ? '. Message: ' + error.response.data.toString().trim() : ''}. Try ${retryNo+1} of ${maxRetries+1}.`; - if (retryNo < maxRetries) { - logger.log(`${logMessage} Retrying…`); - return nodeManager.changeNodes() - .then(function() { - return module.exports(nodeManager)(passPhrase, addressOrPublicKey, message, messageType, amount, isAmountInADM, maxRetries, ++retryNo); - }); - } - logger.warn(`${logMessage} No more attempts, returning error.`); - return validator.formatRequestResults(error, false); - }); - } catch (e) { - return { - success: false, - errorMessage: `Unable to encode message '${message}' with public key ${publicKey}, or unable to build a transaction. Exception: ` + e, - }; - } - }; // sendMessage() -}; diff --git a/src/groups/sendTokens.js b/src/groups/sendTokens.js deleted file mode 100644 index 11d5fce..0000000 --- a/src/groups/sendTokens.js +++ /dev/null @@ -1,89 +0,0 @@ -const axios = require('../helpers/axiosClient'); -const logger = require('../helpers/logger'); -const keys = require('../helpers/keys'); -const constants = require('../helpers/constants'); -const transactionFormer = require('../helpers/transactionFormer'); -const validator = require('../helpers/validator'); - -const DEFAULT_SEND_TOKENS_RETRIES = 4; // How much re-tries for send tokens requests by default. Total 4+1 tries - -module.exports = (nodeManager) => { - /** - * Creates Token Transfer transaction, signs it, and broadcasts to ADAMANT network - * See https://github.com/Adamant-im/adamant/wiki/Transaction-Types#type-0-token-transfer-transaction - * @param {string} passPhrase Senders's passPhrase. Sender's address will be derived from it. - * @param {string} addressOrPublicKey Recipient's ADAMANT address or public key. - * Address is preferred, as if we get public key, we should derive address from it. - * @param {string | number} amount Amount to send - * @param {boolean} isAmountInADM If amount specified in ADM, or in sats (10^-8 ADM) - * @param {number} maxRetries How much times to retry request - * @param {number} retryNo Number of request already made - * @return {Promise} Request results - */ - return (passPhrase, addressOrPublicKey, amount, isAmountInADM = true, maxRetries = DEFAULT_SEND_TOKENS_RETRIES, retryNo = 0) => { - let transaction; - let address; let publicKey; - - try { - if (!validator.validatePassPhrase(passPhrase)) { - return validator.badParameter('passPhrase'); - } - - const keyPair = keys.createKeypairFromPassPhrase(passPhrase); - - if (!validator.validateAdmAddress(addressOrPublicKey)) { - if (!validator.validateAdmPublicKey(addressOrPublicKey)) { - return validator.badParameter('addressOrPublicKey', addressOrPublicKey); - } else { - publicKey = addressOrPublicKey; - try { - address = keys.createAddressFromPublicKey(publicKey); - } catch (e) { - return validator.badParameter('addressOrPublicKey', addressOrPublicKey); - } - } - } else { - publicKey = ''; - address = addressOrPublicKey; - } - - let amountInSat = amount; - - if (isAmountInADM) { - amountInSat = validator.admToSats(amount); - } - - if (!validator.validateIntegerAmount(amountInSat)) { - return validator.badParameter('amount', amount); - } - - const data = { - keyPair, - recipientId: address, - amount: amountInSat, - }; - - transaction = transactionFormer.createTransaction(constants.transactionTypes.SEND, data); - } catch (e) { - return validator.badParameter('#exception_catched#', e); - } - - const url = nodeManager.node() + '/api/transactions/process'; - return axios.post(url, {transaction}) - .then(function(response) { - return validator.formatRequestResults(response, true); - }) - .catch(function(error) { - const logMessage = `[ADAMANT js-api] Send tokens request: Request to ${url} failed with ${error.response ? error.response.status : undefined} status code, ${error.toString()}${error.response && error.response.data ? '. Message: ' + error.response.data.toString().trim() : ''}. Try ${retryNo + 1} of ${maxRetries + 1}.`; - if (retryNo < maxRetries) { - logger.log(`${logMessage} Retrying…`); - return nodeManager.changeNodes() - .then(function() { - return module.exports(nodeManager)(passPhrase, addressOrPublicKey, amount, isAmountInADM, maxRetries, ++retryNo); - }); - } - logger.warn(`${logMessage} No more attempts, returning error.`); - return validator.formatRequestResults(error, false); - }); - }; -}; diff --git a/src/groups/voteForDelegate.js b/src/groups/voteForDelegate.js deleted file mode 100644 index 5ef15c0..0000000 --- a/src/groups/voteForDelegate.js +++ /dev/null @@ -1,121 +0,0 @@ -const axios = require('../helpers/axiosClient'); -const get = require('./get'); -const logger = require('../helpers/logger'); -const keys = require('../helpers/keys'); -const constants = require('../helpers/constants'); -const transactionFormer = require('../helpers/transactionFormer'); -const validator = require('../helpers/validator'); - -const DEFAULT_VOTE_FOR_DELEGATE_RETRIES = 4; // How much re-tries for send tokens requests by default. Total 4+1 tries - -const publicKeysCache = { }; - -module.exports = (nodeManager) => { - /** - * Creates votes for delegate transaction, signs it, and broadcasts to ADAMANT network - * See https://github.com/Adamant-im/adamant/wiki/Transaction-Types#type-3-vote-for-delegate-transaction - * @param {string} passPhrase Senders's passPhrase. Sender's address will be derived from it. - * @param {string[]} votes PublicKeys, ADM addresses and delegate names for upvote and downvote. - * It would be more efficient to pass publicKey, otherwise the api will make additional queries - * @param {number} maxRetries How much times to retry request - * @param {number} retryNo Number of request already made - * @return {Promise} Request results - */ - return async (passPhrase, votes, maxRetries = DEFAULT_VOTE_FOR_DELEGATE_RETRIES, retryNo = 0) => { - let transaction; - - try { - if (!validator.validatePassPhrase(passPhrase)) { - return validator.badParameter('passPhrase'); - } - - const keyPair = keys.createKeypairFromPassPhrase(passPhrase); - - const uniqueVotes = []; - - for (let i = votes.length - 1; i >= 0; i--) { - const vote = votes[i]; - const voteName = vote.slice(1); - const voteDirection = vote.charAt(0); - - const cachedPublicKey = publicKeysCache[voteName]; - - if (cachedPublicKey) { - votes[i] = `${voteDirection}${cachedPublicKey}`; - } else { - if (validator.validateAdmVoteForAddress(vote)) { - const res = await get(nodeManager)('/accounts', {address: voteName}); - - if (res.success) { - const publicKey = res.data.account.publicKey; - - votes[i] = `${voteDirection}${publicKey}`; - publicKeysCache[voteName] = publicKey; - } else { - logger.warn(`[ADAMANT js-api] Failed to get public key for ${vote}. ${res.errorMessage}.`); - - return validator.badParameter('votes'); - } - } else if (validator.validateAdmVoteForDelegateName(vote)) { - const res = await get(nodeManager)('/delegates/get', {username: voteName}); - - if (res.success) { - const publicKey = res.data.delegate.publicKey; - - votes[i] = `${voteDirection}${publicKey}`; - publicKeysCache[voteName] = publicKey; - } else { - logger.warn(`[ADAMANT js-api] Failed to get public key for ${vote}. ${res.errorMessage}.`); - - return validator.badParameter('votes'); - } - } else if (!validator.validateAdmVoteForPublicKey(vote)) { - return validator.badParameter('votes'); - } - } - - // Exclude duplicates - const foundCopy = uniqueVotes.find((v) => v.slice(1) === votes[i].slice(1)); - - if (!foundCopy) { - uniqueVotes.push(votes[i]); - } - } - - const type = constants.transactionTypes.VOTE; - - const data = { - type, - keyPair, - votes: uniqueVotes, - }; - - transaction = transactionFormer.createTransaction(type, data); - } catch (error) { - return validator.badParameter('#exception_catched#', error); - } - - const url = nodeManager.node() + '/api/accounts/delegates'; - - try { - const response = await axios.post(url, transaction); - - return validator.formatRequestResults(response, true); - } catch (error) { - const logMessage = `[ADAMANT js-api] Vote for delegate request: Request to ${url} failed with ${error.response ? error.response.status : undefined} status code, ${error.toString()}${error.response && error.response.data ? '. Message: ' + error.response.data.toString().trim() : ''}. Try ${retryNo+1} of ${maxRetries+1}.`; - - if (retryNo < maxRetries) { - logger.log(`${logMessage} Retrying…`); - - return nodeManager.changeNodes() - .then(() => ( - module.exports(nodeManager)(passPhrase, votes, maxRetries, ++retryNo) - )); - } - - logger.warn(`${logMessage} No more attempts, returning error.`); - - return validator.formatRequestResults(error, false); - } - }; -}; diff --git a/src/helpers/axiosClient.js b/src/helpers/axiosClient.js deleted file mode 100644 index d002737..0000000 --- a/src/helpers/axiosClient.js +++ /dev/null @@ -1,4 +0,0 @@ -const axios = require('axios'); - -const axiosClient = axios.create(); -module.exports = axiosClient; diff --git a/src/helpers/bignumber.js b/src/helpers/bignumber.js deleted file mode 100644 index 3d03e46..0000000 --- a/src/helpers/bignumber.js +++ /dev/null @@ -1,125 +0,0 @@ -/* eslint-disable no-redeclare */ -'use strict'; - -/** - * Buffer functions that implements bignumber. - * @memberof module:helpers - * @requires bignumber - * @constructor -*/ -const BigNumber = require('bignumber.js'); - -/** - * Creates an instance from a Buffer. - * @param {ArrayBuffer} buf - * @param {Object} opts - * @return {ArrayBuffer} new BigNumber instance - * @throws {RangeError} error description multiple of size -*/ -BigNumber.fromBuffer = function(buf, opts) { - if (!opts) opts = {}; - - const endian = {1: 'big', '-1': 'little'}[opts.endian] || opts.endian || 'big'; - - const size = opts.size === 'auto' ? Math.ceil(buf.length) : (opts.size || 1); - - if (buf.length % size !== 0) { - throw new RangeError('Buffer length (' + buf.length + ')' + - ' must be a multiple of size (' + size + ')', - ); - } - - const hex = []; - for (let i = 0; i < buf.length; i += size) { - const chunk = []; - for (let j = 0; j < size; j++) { - chunk.push(buf[i + (endian === 'big' ? j : (size - j - 1))]); - } - - hex.push(chunk - .map(function(c) { - return (c < 16 ? '0' : '') + c.toString(16); - }) - .join(''), - ); - } - - return new BigNumber(hex.join(''), 16); -}; - -/** - * Returns an instance as Buffer. - * @param {Object} opts - * @return {ArrayBuffer} new buffer | error message invalid option -*/ -BigNumber.prototype.toBuffer = function(opts) { - if (typeof opts === 'string') { - if (opts !== 'mpint') { - return 'Unsupported Buffer representation'; - } - - const abs = this.abs(); - const buf = abs.toBuffer({size: 1, endian: 'big'}); - - let len = buf.length === 1 && buf[0] === 0 ? 0 : buf.length; - - if (buf[0] & 0x80) len++; - - const ret = Buffer.alloc(4 + len); - if (len > 0) buf.copy(ret, 4 + (buf[0] & 0x80 ? 1 : 0)); - if (buf[0] & 0x80) ret[4] = 0; - - ret[0] = len & (0xff << 24); - ret[1] = len & (0xff << 16); - ret[2] = len & (0xff << 8); - ret[3] = len & (0xff << 0); - - // Two's compliment for negative integers - const isNeg = this.lt(0); - if (isNeg) { - for (let i = 4; i < ret.length; i++) { - ret[i] = 0xff - ret[i]; - } - } - ret[4] = (ret[4] & 0x7f) | (isNeg ? 0x80 : 0); - if (isNeg) ret[ret.length - 1]++; - - return ret; - } - - if (!opts) { - opts = {}; - } - - const endian = {1: 'big', '-1': 'little'}[opts.endian] || opts.endian || 'big'; - - let hex = this.toString(16); - if (hex.charAt(0) === '-') { - throw new Error( - 'Converting negative numbers to Buffers not supported yet', - ); - } - - const size = opts.size === 'auto' ? Math.ceil(hex.length / 2) : (opts.size || 1); - - const len = Math.ceil(hex.length / (2 * size)) * size; - const buf = Buffer.alloc(len); - - // Zero-pad the hex string so the chunks are all `size` long - while (hex.length < 2 * len) hex = '0' + hex; - - const hx = hex - .split(new RegExp('(.{' + (2 * size) + '})')) - .filter((s) => s.length > 0); - - hx.forEach((chunk, i) => { - for (let j = 0; j < size; j++) { - const ix = i * size + (endian === 'big' ? j : size - j - 1); - buf[ix] = parseInt(chunk.slice(j * 2, j * 2 + 2), 16); - } - }); - - return buf; -}; - -module.exports = BigNumber; diff --git a/src/helpers/bignumber.ts b/src/helpers/bignumber.ts new file mode 100644 index 0000000..dee176e --- /dev/null +++ b/src/helpers/bignumber.ts @@ -0,0 +1,148 @@ +import BigNumber from 'bignumber.js'; + +type Endian = 1 | -1 | 'big' | 'little'; + +interface TransformBufferOptions { + endian?: Endian; + size?: number | 'auto'; +} + +type EndianMap = { + [K in Endian]: 'big' | 'little'; +}; + +const endianMap: EndianMap = { + 1: 'big', + '-1': 'little', + big: 'big', + little: 'little', +}; + +export const fromBuffer = (buf: Buffer, opts: TransformBufferOptions = {}) => { + const endian = resolveEndian(opts); + + const size = resolveSize(opts, buf.length); + + validateBufferLength(buf, size); + + const hex = bufferToHexArray(buf, size, endian); + + return new BigNumber(hex.join(''), 16); +}; + +export const toBuffer = ( + bignumber: BigNumber, + opts: TransformBufferOptions | 'mpint' = {} +) => { + if (typeof opts === 'string') { + return toMpintBuffer(bignumber); + } + + const endian = resolveEndian(opts); + const hex = bignumberToHex(bignumber); + + const size = resolveSize(opts, hex.length / 2); + + return hexToBuffer(hex, size, endian); +}; + +export function resolveEndian(opts: TransformBufferOptions): Endian { + if (!opts.endian) { + return 'big'; + } + + return endianMap[opts.endian]; +} + +export function resolveSize( + opts: TransformBufferOptions, + defaultSize: number +): number { + return opts.size === 'auto' ? Math.ceil(defaultSize) : opts.size || 1; +} + +export function validateBufferLength(buf: Buffer, size: number) { + if (buf.length % size !== 0) { + throw new RangeError( + `Buffer length (${buf.length}) must be a multiple of size (${size})` + ); + } +} + +export function bufferToHexArray( + buf: Buffer, + size: number, + endian: Endian +): string[] { + const hex: string[] = []; + + for (let i = 0; i < buf.length; i += size) { + const chunk: string[] = []; + for (let j = 0; j < size; j++) { + const chunkIndex = endian === 'big' ? j : size - j - 1; + chunk.push(buf[i + chunkIndex].toString(16).padStart(2, '0')); + } + hex.push(chunk.join('')); + } + + return hex; +} + +export function bignumberToHex(bignumber: BigNumber): string { + const hex = bignumber.toString(16); + if (hex.charAt(0) === '-') { + throw new Error('Converting negative numbers to Buffers not supported yet'); + } + return hex; +} + +export function hexToBuffer(hex: string, size: number, endian: Endian): Buffer { + const len = Math.ceil(hex.length / (2 * size)) * size; + const buf = Buffer.alloc(len); + + while (hex.length < 2 * len) { + hex = '0' + hex; + } + + const hx = hex + .split(new RegExp('(.{' + 2 * size + '})')) + .filter(s => s.length > 0); + + hx.forEach((chunk, i) => { + for (let j = 0; j < size; j++) { + const ix = i * size + (endian === 'big' ? j : size - j - 1); + buf[ix] = parseInt(chunk.slice(j * 2, j * 2 + 2), 16); + } + }); + + return buf; +} + +function toMpintBuffer(bignumber: BigNumber): Buffer { + const buf = toBuffer(bignumber.abs(), {size: 1, endian: 'big'}); + + let len = buf.length === 1 && buf[0] === 0 ? 0 : buf.length; + + if (buf[0] & 0x80) len++; + + const ret = Buffer.alloc(4 + len); + if (len > 0) buf.copy(ret, 4 + (buf[0] & 0x80 ? 1 : 0)); + if (buf[0] & 0x80) ret[4] = 0; + + ret[0] = len & (0xff << 24); + ret[1] = len & (0xff << 16); + ret[2] = len & (0xff << 8); + ret[3] = len & (0xff << 0); + + // Two's compliment for negative integers + const isNeg = bignumber.lt(0); + if (isNeg) { + for (let i = 4; i < ret.length; i++) { + ret[i] = 0xff - ret[i]; + } + } + ret[4] = (ret[4] & 0x7f) | (isNeg ? 0x80 : 0); + if (isNeg) ret[ret.length - 1]++; + + return ret; +} diff --git a/src/helpers/constants.js b/src/helpers/constants.js deleted file mode 100644 index 0a3f598..0000000 --- a/src/helpers/constants.js +++ /dev/null @@ -1,45 +0,0 @@ -module.exports = { - epochTime: new Date(Date.UTC(2017, 8, 2, 17, 0, 0, 0)), - fees: { - send: 50000000, - vote: 1000000000, - secondsignature: 500000000, - delegate: 30000000000, - multisignature: 500000000, - dapp: 2500000000, - old_chat_message: 500000, - chat_message: 100000, - profile_update: 5000000, - avatar_upload: 10000000, - state_store: 100000, - }, - transactionTypes: { - SEND: 0, - SIGNATURE: 1, - DELEGATE: 2, - VOTE: 3, - MULTI: 4, - DAPP: 5, - IN_TRANSFER: 6, - OUT_TRANSFER: 7, - CHAT_MESSAGE: 8, - STATE: 9, - }, - maxVotesPerTransaction: 33, - HEALTH_CHECK_TIMEOUT: 4000, // 4 seconds - SAT: 100000000, - RE_HEX: /^[a-fA-F0-9]+$/, - RE_BASE64: /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/, - RE_ADM_ADDRESS: /^U([0-9]{6,})$/, - RE_ADM_VOTE_FOR_PUBLIC_KEY: /^(\+|-)[a-fA-F0-9]{64}$/, - RE_ADM_VOTE_FOR_ADDRESS: /^(\+|-)U([0-9]{6,})$/, - RE_ADM_VOTE_FOR_DELEGATE_NAME: /^(\+|-)([a-z0-9!@$&_]{1,20})$/, - RE_ADM_DELEGATE_NAME: /^[a-z0-9!@$&_]{1,20}$/, - RE_BTC_ADDRESS: /^(bc1|[13])[a-km-zA-HJ-NP-Z02-9]{25,39}$/, - RE_DASH_ADDRESS: /^[7X][1-9A-HJ-NP-Za-km-z]{33,}$/, - RE_DOGE_ADDRESS: /^[A|D|9][A-Z0-9]([0-9a-zA-Z]{9,})$/, - RE_LSK_ADDRESS: /^[0-9]{2,21}L$/, - - RE_HTTP_URL: /^https?:\/\/(.*)$/, - RE_IP: /(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}/, -}; diff --git a/src/helpers/constants.ts b/src/helpers/constants.ts new file mode 100644 index 0000000..b64394a --- /dev/null +++ b/src/helpers/constants.ts @@ -0,0 +1,50 @@ +export enum TransactionType { + SEND, + SIGNATURE, + DELEGATE, + VOTE, + MULTI, + DAPP, + IN_TRANSFER, + OUT_TRANSFER, + CHAT_MESSAGE, + STATE, +} + +export type MessageTypes = 1 | 2 | 3; + +/** + * Message type + * + * @see https://github.com/Adamant-im/adamant/wiki/Message-Types + */ +export enum MessageType { + Chat = 1, + Rich = 2, + Signal = 3, +} + +export const MAX_VOTES_PER_TRANSACTION = 33; + +/** + * 4 seconds + */ +export const HEALTH_CHECK_TIMEOUT = 4000; + +export const DEFAULT_GET_REQUEST_RETRIES = 3; + +export const SAT = 100_000_000; + +export const fees = { + send: 50000000, + vote: 1000000000, + secondsignature: 500000000, + delegate: 30000000000, + multisignature: 500000000, + dapp: 2500000000, + old_chat_message: 500000, + chat_message: 100000, + profile_update: 5000000, + avatar_upload: 10000000, + state_store: 100000, +}; diff --git a/src/helpers/encryptor.js b/src/helpers/encryptor.js deleted file mode 100644 index 265c64e..0000000 --- a/src/helpers/encryptor.js +++ /dev/null @@ -1,42 +0,0 @@ -const sodium = require('sodium-browserify-tweetnacl'); -const nacl = require('tweetnacl/nacl-fast'); -const ed2curve = require('ed2curve'); - -module.exports = { - bytesToHex(bytes) { - let hex = ''; - - for (const byte of bytes) { - hex += (byte >>> 4).toString(16); - hex += (byte & 0xF).toString(16); - } - - return hex; - }, - hexToBytes(hex) { - const bytes = []; - - for (let c = 0; c < hex.length; c += 2) { - bytes.push(parseInt(hex.substr(c, 2), 16)); - } - - return bytes; - }, - encodeMessage(msg, keypair, recipientPublicKey) { - const nonce = Buffer.allocUnsafe(24); - sodium.randombytes(nonce); - - const plainText = Buffer.from(msg.toString()); - const DHSecretKey = ed2curve.convertSecretKey(keypair.privateKey); - const DHPublicKey = ed2curve.convertPublicKey( - new Uint8Array(this.hexToBytes(recipientPublicKey)), - ); - - const encrypted = nacl.box(plainText, nonce, DHPublicKey, DHSecretKey); - - return { - message: this.bytesToHex(encrypted), - own_message: this.bytesToHex(nonce), - }; - }, -}; diff --git a/src/helpers/encryptor.ts b/src/helpers/encryptor.ts new file mode 100644 index 0000000..d32c7fb --- /dev/null +++ b/src/helpers/encryptor.ts @@ -0,0 +1,132 @@ +import sodium from 'sodium-browserify-tweetnacl'; +import nacl from 'tweetnacl'; +import ed2curve from 'ed2curve'; +import {KeyPair, createKeypairFromPassphrase} from './keys'; + +export const bytesToHex = (bytes: Uint8Array) => { + let hex = ''; + + for (const byte of bytes) { + hex += (byte >>> 4).toString(16); + hex += (byte & 0xf).toString(16); + } + + return hex; +}; + +export const hexToBytes = (hex: string) => { + const bytes: number[] = []; + + for (let c = 0; c < hex.length; c += 2) { + bytes.push(parseInt(hex.slice(c, c + 2), 16)); + } + + return Uint8Array.from(bytes); +}; + +export const utf8ArrayToStr = (array: Uint8Array) => { + const len = array.length; + let out = ''; + let i = 0; + let c: number; + let char2: number; + let char3: number; + + while (i < len) { + c = array[i++]; + switch (c >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + out += String.fromCharCode(c); + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + char2 = array[i++]; + out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f)); + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = array[i++]; + char3 = array[i++]; + out += String.fromCharCode( + ((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0) + ); + break; + } + } + + return out; +}; + +export const encodeMessage = ( + msg: string, + keypair: KeyPair, + recipientPublicKey: string +) => { + const nonce = Buffer.allocUnsafe(24); + sodium.randombytes(nonce); + + const plainText = Buffer.from(msg.toString()); + const DHSecretKey = ed2curve.convertSecretKey(keypair.privateKey); + const DHPublicKey = ed2curve.convertPublicKey( + new Uint8Array(hexToBytes(recipientPublicKey)) + ); + + if (!DHPublicKey) { + throw new Error('encodeMessage: invalid key'); + } + + const encrypted = nacl.box(plainText, nonce, DHPublicKey, DHSecretKey); + + return { + message: bytesToHex(encrypted), + own_message: bytesToHex(nonce), + }; +}; + +export const decodeMessage = ( + message: string, + senderPublicKey: string, + passphrase: string, + nonce: string +) => { + const keypair = createKeypairFromPassphrase(passphrase); + + if (typeof message !== 'string') { + throw new Error('decodeMessage message should be a string'); + } + + if (typeof nonce !== 'string') { + throw new Error('decodeMessage: nonce should be a string'); + } + + if (typeof senderPublicKey !== 'string') { + throw new Error('decodeMessage: senderPublicKey should be a string'); + } + + const DHPublicKey = ed2curve.convertPublicKey(hexToBytes(senderPublicKey)); + + if (!DHPublicKey) { + throw new Error('decodeMessage: invalid key'); + } + + const {privateKey} = keypair; + const DHSecretKey = ed2curve.convertSecretKey(privateKey); + + const decrypted = nacl.box.open( + hexToBytes(message), + hexToBytes(nonce), + DHPublicKey, + DHSecretKey + ); + + return decrypted ? utf8ArrayToStr(decrypted) : ''; +}; diff --git a/src/helpers/healthCheck.js b/src/helpers/healthCheck.js deleted file mode 100644 index c8c4bfa..0000000 --- a/src/helpers/healthCheck.js +++ /dev/null @@ -1,234 +0,0 @@ -const dnsPromises = require('dns').promises; - -const axios = require('../helpers/axiosClient'); -const socket = require('./wsClient'); -const logger = require('./logger'); -const validator = require('./validator'); - -const {RE_IP, RE_HTTP_URL, HEALTH_CHECK_TIMEOUT} = require('./constants'); - -const CHECK_NODES_INTERVAL = 60 * 5 * 1000; // Update active nodes every 5 minutes -const HEIGHT_EPSILON = 5; // Used to group nodes by height and choose synced - -module.exports = ( - nodes, - checkHealthAtStartup = true, - timeout = HEALTH_CHECK_TIMEOUT, - checkHealthAtStartupCallback, -) => { - const nodesList = nodes; - let isCheckingNodes = false; - let startupCallback = checkHealthAtStartupCallback; - - // Note: it may be not synced; and before first health check a node can reply with obsolete data - let [activeNode] = nodesList; - - /** - * Updates active nodes. If nodes are already updating, returns Promise of previous call - * @param {boolean} isPlannedUpdate - * @param {boolean} isFirstUpdate - * @return {Promise} Call changeNodes().then to do something when update complete - */ - async function changeNodes(isPlannedUpdate = false, isFirstUpdate = false) { - if (!isCheckingNodes) { - isCheckingNodes = true; - - if (!isPlannedUpdate) { - logger.warn('[ADAMANT js-api] Health check: Forcing to update active nodes…'); - } - - await checkNodes(!isPlannedUpdate); - - if (isFirstUpdate) { - startupCallback?.(); - } - - isCheckingNodes = false; - - return true; - } - } - - /** - * Requests every ADAMANT node for its status, makes a list of live nodes, and chooses one active - * @param {boolean} forceChangeActiveNode - */ - async function checkNodes(forceChangeActiveNode) { - const liveNodes = []; - - try { - for (const node of nodesList) { - try { - const start = unixTimestamp(); - - const req = await checkNode(`${node}/api/node/status`, timeout); - - const [url] = node.replace(RE_HTTP_URL, '$1').split(':'); - const ifIP = RE_IP.test(url); - - const ip = ifIP ? url : await getIP(url); - const ifHttps = node.startsWith('https'); - - if (req.status) { - liveNodes.push({ - node, - ifIP, - url, - ip, - ifHttps, - outOfSync: false, - ping: unixTimestamp() - start, - height: req.status.network.height, - heightEpsilon: Math.round(req.status.network.height / HEIGHT_EPSILON), - socketSupport: req.status.wsClient?.enabled, - wsPort: req.status.wsClient?.port, - }); - } else { - logger.log(`[ADAMANT js-api] Health check: Node ${node} haven't returned its status`); - } - } catch (e) { - logger.log(`[ADAMANT js-api] Health check: Error while checking node ${node}, ${e}`); - } - } - - const count = liveNodes.length; - - let outOfSyncCount = 0; - - if (!count) { - logger.error(`[ADAMANT js-api] Health check: All of ${nodesList.length} nodes are unavailable. Check internet connection and nodes list in config.`); - } else { - // Set activeNode to one that have maximum height and minimum ping - if (count === 1) { - activeNode = liveNodes[0].node; - } else if (count === 2) { - const [h0, h1] = liveNodes; - - activeNode = h0.height > h1.height ? h0.node : h1.node; - - // Mark node outOfSync if needed - if (h0.heightEpsilon > h1.heightEpsilon) { - liveNodes[1].outOfSync = true; - outOfSyncCount += 1; - } else if (h0.heightEpsilon < h1.heightEpsilon) { - liveNodes[0].outOfSync = true; - outOfSyncCount += 1; - } - } else { - let biggestGroup = []; - // Removing lodash: const groups = _.groupBy(liveNodes, n => n.heightEpsilon); - const groups = liveNodes.reduce((grouped, node) => { - const int = Math.floor(node.heightEpsilon); // Excessive, it is already rounded - - if (!Object.prototype.hasOwnProperty.call(grouped, int)) { - grouped[int] = []; - } - - grouped[int].push(node); - - return grouped; - }, {}); - - Object.keys(groups).forEach((key) => { - if (groups[key].length > biggestGroup.length) { - biggestGroup = groups[key]; - } - }); - - // All the nodes from the biggestGroup list are considered to be in sync, all the others are not - liveNodes.forEach((node) => { - node.outOfSync = !biggestGroup.includes(node); - }); - - outOfSyncCount = liveNodes.length - biggestGroup.length; - - biggestGroup.sort((a, b) => a.ping - b.ping); - liveNodes.sort((a, b) => a.ping - b.ping); - - if (forceChangeActiveNode && biggestGroup.length > 1 && activeNode === biggestGroup[0].node) { - // Use random node from which are synced - activeNode = biggestGroup[validator.getRandomIntInclusive(1, biggestGroup.length - 1)].node; - } else { - // Use node with minimum ping among which are synced - activeNode = biggestGroup[0].node; - } - } - - socket.reviseConnection(liveNodes); - - const unavailableCount = nodesList.length - liveNodes.length; - const supportedCount = liveNodes.length - outOfSyncCount; - - let nodesInfoString = ''; - - if (unavailableCount) { - nodesInfoString += `, ${unavailableCount} nodes didn't respond`; - } - - if (outOfSyncCount) { - nodesInfoString += `, ${outOfSyncCount} nodes are not synced`; - } - - logger.log(`[ADAMANT js-api] Health check: Found ${supportedCount} supported and synced nodes${nodesInfoString}. Active node is ${activeNode}.`); - } - } catch (e) { - logger.warn('[ADAMANT js-api] Health check: Error in checkNodes(), ' + e); - } - } - - function setStartupCallback(callback) { - if (!isCheckingNodes) { - callback(); - } else { - startupCallback = callback; - } - } - - if (checkHealthAtStartup) { - changeNodes(true, true); - - setInterval( - () => changeNodes(true), - CHECK_NODES_INTERVAL, - ); - } else { - startupCallback?.(); - } - - return { - /** - * @return {string} Current active node, f. e. http://88.198.156.44:36666 - */ - node: () => activeNode, - setStartupCallback, - changeNodes, - }; -}; - -async function getIP(url) { - try { - const addresses = await dnsPromises.resolve4(url); - - if (addresses && addresses[0] !== '0.0.0.0') { - return addresses[0]; - } - } catch (error) { - return; - } -} - -/** - * Requests status from a single ADAMANT node - * @param {string} url Node URL to request - * @param {number} timeout Request timeout - * @return {Promise} Node's status information - */ -function checkNode(url, timeout) { - return axios.get(url, {timeout}) - .then((response) => ({status: response.data})) - .catch((err) => false); -} - -function unixTimestamp() { - return new Date().getTime(); -} diff --git a/src/helpers/healthCheck.ts b/src/helpers/healthCheck.ts new file mode 100644 index 0000000..c390f5c --- /dev/null +++ b/src/helpers/healthCheck.ts @@ -0,0 +1,269 @@ +import axios from 'axios'; +import {HEALTH_CHECK_TIMEOUT} from './constants'; +import {Logger} from './logger'; +import {unixTimestamp} from './time'; +import {parseUrl} from './url'; + +import {GetNodeStatusResponseDto} from '../api/generated'; +import {AdamantApiResult, getRandomIntInclusive} from './validator'; +import {WebSocketClient, WsOptions} from './wsClient'; + +export interface NodeManagerOptions { + nodes: string[]; + timeout?: number; + socket?: WebSocketClient; + checkHealthAtStartup?: boolean; +} + +export interface ActiveNode { + node: string; + ping: number; + baseURL: string; + ip?: string; + isHttps: boolean; + height: number; + heightEpsilon: number; + socketSupport: boolean; + wsPort: number; + outOfSync?: boolean; +} + +const CHECK_NODES_INTERVAL = 60 * 5 * 1000; // Update active nodes every 5 minutes +const HEIGHT_EPSILON = 5; // Used to group nodes by height and choose synced + +export class NodeManager { + options: NodeManagerOptions; + + public node: string; + public socket?: WebSocketClient; + + protected logger: Logger; + + private onReadyCallback?: () => void; + + private initialized = false; + private isCheckingNodes = false; + + constructor(logger: Logger, options: NodeManagerOptions) { + this.options = { + timeout: HEALTH_CHECK_TIMEOUT, + checkHealthAtStartup: true, + ...options, + }; + + const {socket, nodes, checkHealthAtStartup} = this.options; + + this.logger = logger; + this.socket = socket; + + this.node = nodes[0]; + + if (checkHealthAtStartup) { + this.updateNodes(true); + + setInterval(() => this.updateNodes(true), CHECK_NODES_INTERVAL); + } else { + this.ready(); + } + } + + public onReady(callback: () => void) { + this.onReadyCallback = callback; + } + + public initSocket(options: WsOptions) { + this.socket = new WebSocketClient({logger: this.logger, ...options}); + } + + private ready() { + if (this.onReadyCallback) { + this.onReadyCallback(); + } + + this.initialized = true; + } + + async updateNodes(isPlannedUpdate = false) { + if (this.isCheckingNodes) { + return; + } + + this.isCheckingNodes = true; + + if (!isPlannedUpdate) { + this.logger.warn( + '[ADAMANT js-api] Health check: Forcing to update active nodes…' + ); + } + + const activeNodes = await this.checkNodes(); + + await this.chooseNode(activeNodes, !isPlannedUpdate); + + if (!this.initialized) { + this.ready(); + } + + this.isCheckingNodes = false; + } + + async chooseNode(activeNodes: ActiveNode[], forceChangeActiveNode?: boolean) { + const {logger, socket} = this; + + const {length: activeNodesCount} = activeNodes; + if (!activeNodesCount) { + logger.error( + `[ADAMANT js-api] Health check: All of ${activeNodesCount} nodes are unavailable. Check internet connection and nodes list in config.` + ); + return; + } + + let outOfSyncCount = 0; + + if (activeNodesCount === 1) { + this.node = activeNodes[0].node; + } else if (activeNodesCount === 2) { + const [h0, h1] = activeNodes; + + this.node = h0.height > h1.height ? h0.node : h1.node; + + // Mark node outOfSync if needed + if (h0.heightEpsilon > h1.heightEpsilon) { + activeNodes[1].outOfSync = true; + outOfSyncCount += 1; + } else if (h0.heightEpsilon < h1.heightEpsilon) { + activeNodes[0].outOfSync = true; + outOfSyncCount += 1; + } + } else { + // Removing lodash: const groups = _.groupBy(liveNodes, n => n.heightEpsilon); + const groups = activeNodes.reduce( + (grouped: {[heightEpsilon: number]: ActiveNode[]}, node) => { + const {heightEpsilon} = node; + + if (!grouped[heightEpsilon]) { + grouped[heightEpsilon] = []; + } + + grouped[heightEpsilon].push(node); + + return grouped; + }, + {} + ); + + let biggestGroup: ActiveNode[] = []; + let biggestGroupSize = 0; + + for (const key in groups) { + if (Object.prototype.hasOwnProperty.call(groups, key)) { + const group = groups[key]; + + if (groups[key].length > biggestGroupSize) { + biggestGroup = group; + biggestGroupSize = group.length; + } + } + } + + // All the nodes from the biggestGroup list are considered to be in sync, all the others are not + for (const node of activeNodes) { + node.outOfSync = !biggestGroup.includes(node); + } + + outOfSyncCount = activeNodes.length - biggestGroup.length; + + biggestGroup.sort((a, b) => a.ping - b.ping); + + if ( + forceChangeActiveNode && + biggestGroup.length > 1 && + this.node === biggestGroup[0].node + ) { + // Use random node from which are synced + const randomIndex = getRandomIntInclusive(1, biggestGroup.length - 1); + this.node = biggestGroup[randomIndex].node; + } else { + // Use node with minimum ping among synced + this.node = biggestGroup[0].node; + } + } + + socket?.reviseConnection(activeNodes); + + const {nodes} = this.options; + + const unavailableCount = nodes.length - activeNodesCount; + const supportedCount = activeNodesCount - outOfSyncCount; + + let nodesInfoString = ''; + + if (unavailableCount) { + nodesInfoString += `, ${unavailableCount} nodes didn't respond`; + } + + if (outOfSyncCount) { + nodesInfoString += `, ${outOfSyncCount} nodes are not synced`; + } + + this.logger.log( + `[ADAMANT js-api] Health check: Found ${supportedCount} supported and synced nodes${nodesInfoString}. Active node is ${this.node}.` + ); + } + + async getNodeStatus(node: string) { + try { + const {timeout} = this.options; + + const response = await axios.get< + AdamantApiResult + >(`${node}/api/node/status`, { + timeout, + }); + + return response.data; + } catch (error) { + return {success: false} as {success: false}; + } + } + + async checkNodes() { + const {nodes} = this.options; + + const activeNodes: ActiveNode[] = []; + + for (const node of nodes) { + const start = unixTimestamp(); + + const response = await this.getNodeStatus(node); + + const ping = unixTimestamp() - start; + + if (!response.success) { + this.logger.log( + `[ADAMANT js-api] Health check: Node ${node} haven't returned its status` + ); + continue; + } + + const {wsClient, network} = response; + + const socketSupport = wsClient.enabled; + const wsPort = wsClient.port; + + const {height} = network; + + activeNodes.push({ + ...(await parseUrl(node)), + node, + ping, + height, + heightEpsilon: Math.round(height / HEIGHT_EPSILON), + socketSupport, + wsPort, + }); + } + + return activeNodes; + } +} diff --git a/src/helpers/keys.js b/src/helpers/keys.js deleted file mode 100644 index 9df0573..0000000 --- a/src/helpers/keys.js +++ /dev/null @@ -1,49 +0,0 @@ -const sodium = require('sodium-browserify-tweetnacl'); -const crypto = require('crypto'); -const Mnemonic = require('bitcore-mnemonic'); - -const bignum = require('./bignumber.js'); - -module.exports = { - createNewPassPhrase() { - return new Mnemonic(Mnemonic.Words.ENGLISH).toString(); - }, - makeKeypairFromHash(hash) { - const keypair = sodium.crypto_sign_seed_keypair(hash); - - return { - publicKey: keypair.publicKey, - privateKey: keypair.secretKey, - }; - }, - createHashFromPassPhrase(passPhrase) { - const secretMnemonic = new Mnemonic(passPhrase, Mnemonic.Words.ENGLISH); - - return crypto - .createHash('sha256') - .update( - secretMnemonic.toSeed().toString('hex'), - 'hex', - ) - .digest(); - }, - createKeypairFromPassPhrase(passPhrase) { - const hash = this.createHashFromPassPhrase(passPhrase); - - return this.makeKeypairFromHash(hash); - }, - createAddressFromPublicKey(publicKey) { - const publicKeyHash = crypto - .createHash('sha256') - .update(publicKey, 'hex') - .digest(); - - const temp = Buffer.alloc(8); - - for (let i = 0; i < 8; i++) { - temp[i] = publicKeyHash[7 - i]; - } - - return `U${bignum.fromBuffer(temp)}`; - }, -}; diff --git a/src/helpers/keys.ts b/src/helpers/keys.ts new file mode 100644 index 0000000..b0bca35 --- /dev/null +++ b/src/helpers/keys.ts @@ -0,0 +1,56 @@ +import sodium from 'sodium-browserify-tweetnacl'; +import crypto from 'crypto'; +import {mnemonicToSeedSync, generateMnemonic} from 'bip39'; + +import * as bignum from './bignumber'; +import type {AdamantAddress} from '../api'; + +export interface KeyPair { + publicKey: Buffer; + privateKey: Buffer; +} + +export const createNewPassphrase = () => generateMnemonic(); + +export const makeKeypairFromHash = (hash: Buffer): KeyPair => { + const keypair = sodium.crypto_sign_seed_keypair(hash); + + return { + publicKey: keypair.publicKey, + privateKey: keypair.secretKey, + }; +}; + +export const createHashFromPassphrase = (passphrase: string) => + crypto + .createHash('sha256') + .update(mnemonicToSeedSync(passphrase).toString('hex'), 'hex') + .digest(); + +export const createKeypairFromPassphrase = (passphrase: string) => { + const hash = createHashFromPassphrase(passphrase); + + return makeKeypairFromHash(hash); +}; + +export const createAddressFromPublicKey = ( + publicKey: Buffer | string +): AdamantAddress => { + const hash = crypto.createHash('sha256'); + + if (typeof publicKey === 'string') { + hash.update(publicKey, 'hex'); + } else { + hash.update(publicKey); + } + + const publicKeyBuffer = hash.digest(); + + const temp = Buffer.alloc(8); + + for (let i = 0; i < 8; i++) { + temp[i] = publicKeyBuffer[7 - i]; + } + + return `U${bignum.fromBuffer(temp)}`; +}; diff --git a/src/helpers/logger.js b/src/helpers/logger.js deleted file mode 100644 index 4278e1e..0000000 --- a/src/helpers/logger.js +++ /dev/null @@ -1,36 +0,0 @@ -const logger = { - errorLevel: 'log', - logger: console, - - initLogger(errorLevel, log) { - if (errorLevel) { - this.errorLevel = errorLevel; - } - - if (log) { - this.logger = log; - } - }, - error(str) { - if (['error', 'warn', 'info', 'log'].includes(this.errorLevel)) { - this.logger.error(str); - } - }, - warn(str) { - if (['warn', 'info', 'log'].includes(this.errorLevel)) { - this.logger.warn(str); - } - }, - info(str) { - if (['info', 'log'].includes(this.errorLevel)) { - this.logger.info(str); - } - }, - log(str) { - if (this.errorLevel === 'log') { - this.logger.log(str); - } - }, -}; - -module.exports = logger; diff --git a/src/helpers/logger.ts b/src/helpers/logger.ts new file mode 100644 index 0000000..3ded924 --- /dev/null +++ b/src/helpers/logger.ts @@ -0,0 +1,52 @@ +export enum LogLevel { + Error, + Warn, + Info, + Log, +} + +export type CustomLogger = Record< + 'error' | 'warn' | 'info' | 'log', + (str: string) => void +>; + +export const logLevels = ['error', 'warn', 'info', 'log'] as const; + +export type LogLevelName = (typeof logLevels)[number]; + +export class Logger { + logger: CustomLogger; + level: LogLevel; + + constructor( + level: LogLevel | LogLevelName = LogLevel.Log, + logger: CustomLogger = console + ) { + this.level = typeof level === 'number' ? level : logLevels.indexOf(level); + this.logger = logger; + } + + error(message: string) { + if (this.level >= LogLevel.Error) { + this.logger.error(message); + } + } + + warn(message: string) { + if (this.level >= LogLevel.Warn) { + this.logger.warn(message); + } + } + + info(message: string) { + if (this.level >= LogLevel.Info) { + this.logger.info(message); + } + } + + log(message: string) { + if (this.level >= LogLevel.Log) { + this.logger.log(message); + } + } +} diff --git a/src/helpers/tests/keys.test.js b/src/helpers/tests/keys.test.js deleted file mode 100644 index eb4ee6d..0000000 --- a/src/helpers/tests/keys.test.js +++ /dev/null @@ -1,62 +0,0 @@ -const keys = require('../keys'); -const {validateAdmAddress} = require('../validator'); - -describe('createNewPassPhrase', () => { - test('Should return string that contains more than 11 words', () => { - const passPhrase = keys.createNewPassPhrase(); - - expect(typeof passPhrase).toBe('string'); - expect( - passPhrase.split(' ').length, - ).toBeGreaterThanOrEqual(12); - }); -}); - -describe('makeKeypairFromHash', () => { - test('Should return object with buffers publicKey and privateKey', () => { - const passPhrase = keys.createNewPassPhrase(); - const hash = keys.createHashFromPassPhrase(passPhrase); - - const keypair = keys.makeKeypairFromHash(hash); - - expect(typeof keypair).toBe('object'); - - expect(Buffer.isBuffer(keypair.publicKey)).toBe(true); - expect(Buffer.isBuffer(keypair.privateKey)).toBe(true); - }); -}); - -describe('createHashFromPassPhrase', () => { - test('Should return different hashes for different passPhrases', () => { - const passPhrase = keys.createNewPassPhrase(); - const passPhrase2 = keys.createNewPassPhrase(); - - const hash = keys.createHashFromPassPhrase(passPhrase); - const hash2 = keys.createHashFromPassPhrase(passPhrase2); - - expect(hash.equals(hash2)).toBe(false); - }); -}); - -describe('createKeypairFromPassPhrase', () => { - test('Should return keypair with publicKey and privateKey', () => { - const passPhrase = keys.createNewPassPhrase(); - const keypair = keys.createKeypairFromPassPhrase(passPhrase); - - expect(typeof keypair).toBe('object'); - - expect(Buffer.isBuffer(keypair.publicKey)).toBe(true); - expect(Buffer.isBuffer(keypair.privateKey)).toBe(true); - }); -}); - -describe('createAddressFromPublicKey', () => { - test('Should return a string which matches the address pattern', () => { - const passPhrase = keys.createNewPassPhrase(); - const keypair = keys.createKeypairFromPassPhrase(passPhrase); - - const address = keys.createAddressFromPublicKey(keypair.publicKey); - - expect(validateAdmAddress(address)).toBe(true); - }); -}); diff --git a/src/helpers/tests/keys.test.ts b/src/helpers/tests/keys.test.ts new file mode 100644 index 0000000..2d52952 --- /dev/null +++ b/src/helpers/tests/keys.test.ts @@ -0,0 +1,48 @@ +import * as keys from '../keys'; +import {mocked} from './mock-data/address'; + +describe('createNewPassphrase', () => { + test('should return string that contains more than 11 words', () => { + const passphrase = keys.createNewPassphrase(); + + expect(typeof passphrase).toBe('string'); + expect(passphrase.split(' ').length).toBeGreaterThanOrEqual(12); + }); +}); + +describe('makeKeypairFromHash', () => { + test('should create keypair with exact publicKey/privateKey values from hash', () => { + const keypair = keys.makeKeypairFromHash(mocked.hash); + + expect(keypair.publicKey).toStrictEqual(mocked.publicKey); + expect(keypair.privateKey).toStrictEqual(mocked.privateKey); + }); +}); + +describe('createHashFromPassphrase', () => { + test('should create exact hash from the pass phrase', () => { + const passphrase = + 'wrap track hamster grocery casual talk theory half artist toast art essence'; + + const hash = keys.createHashFromPassphrase(passphrase); + + expect(hash).toStrictEqual(mocked.hash); + }); +}); + +describe('createKeypairFromPassphrase', () => { + test('should create keypair with exact publicKey/privateKey values from pass phrase', () => { + const keypair = keys.createKeypairFromPassphrase(mocked.passphrase); + + expect(keypair.publicKey).toStrictEqual(mocked.publicKey); + expect(keypair.privateKey).toStrictEqual(mocked.privateKey); + }); +}); + +describe('createAddressFromPublicKey', () => { + test('should return a string which matches the address pattern', () => { + const address = keys.createAddressFromPublicKey(mocked.publicKey); + + expect(address).toBe(mocked.address); + }); +}); diff --git a/src/helpers/tests/logger.test.js b/src/helpers/tests/logger.test.js deleted file mode 100644 index a463297..0000000 --- a/src/helpers/tests/logger.test.js +++ /dev/null @@ -1,203 +0,0 @@ -const logger = require('../logger'); - -describe('logger: log', () => { - const logLevel = 'log'; - - test('Should log log level', (done) => { - logger.initLogger(logLevel, { - log(str) { - expect(str).toBe('log'); - - done(); - }, - }); - - logger.log('log'); - }); - - test('Should log info level', (done) => { - logger.initLogger(logLevel, { - info(str) { - expect(str).toBe('info'); - - done(); - }, - }); - - logger.info('info'); - }); - - test('Should log warn level', (done) => { - logger.initLogger(logLevel, { - warn(str) { - expect(str).toBe('warn'); - - done(); - }, - }); - - logger.warn('warn'); - }); - - test('Should log error level', (done) => { - logger.initLogger(logLevel, { - error(str) { - expect(str).toBe('error'); - - done(); - }, - }); - - logger.error('error'); - }); -}); - -describe('logger: info', () => { - const logLevel = 'info'; - - test('Should not log log level', (done) => { - logger.initLogger(logLevel, { - log() { - done('Log level has been called'); - }, - }); - - logger.log('log'); - done(); - }); - - test('Should log info level', (done) => { - logger.initLogger(logLevel, { - info(str) { - expect(str).toBe('info'); - - done(); - }, - }); - - logger.info('info'); - }); - - test('Should log warn level', (done) => { - logger.initLogger(logLevel, { - warn(str) { - expect(str).toBe('warn'); - - done(); - }, - }); - - logger.warn('warn'); - }); - - test('Should log error level', (done) => { - logger.initLogger(logLevel, { - error(str) { - expect(str).toBe('error'); - - done(); - }, - }); - - logger.error('error'); - }); -}); - -describe('logger: warn', () => { - const logLevel = 'warn'; - - test('Should not log log level', (done) => { - logger.initLogger(logLevel, { - log() { - done('Log level has been called'); - }, - }); - - logger.log('log'); - done(); - }); - - test('Should not log info level', (done) => { - logger.initLogger(logLevel, { - info() { - done('Info level has been called'); - }, - }); - - logger.info('info'); - done(); - }); - - test('Should log warn level', (done) => { - logger.initLogger(logLevel, { - warn(str) { - expect(str).toBe('warn'); - - done(); - }, - }); - - logger.warn('warn'); - }); - - test('Should log error level', (done) => { - logger.initLogger(logLevel, { - error(str) { - expect(str).toBe('error'); - - done(); - }, - }); - - logger.error('error'); - }); -}); - -describe('logger: error', () => { - const logLevel = 'error'; - - test('Should not log log level', (done) => { - logger.initLogger(logLevel, { - log() { - done('Log level has been called'); - }, - }); - - logger.log('log'); - done(); - }); - - test('Should not log info level', (done) => { - logger.initLogger(logLevel, { - info() { - done('Info level has been called'); - }, - }); - - logger.info('info'); - done(); - }); - - test('Should not log warn level', (done) => { - logger.initLogger(logLevel, { - warn() { - done('Warn level has been called'); - }, - }); - - logger.warn('warn'); - done(); - }); - - test('Should log error level', (done) => { - logger.initLogger(logLevel, { - error(str) { - expect(str).toBe('error'); - - done(); - }, - }); - - logger.error('error'); - }); -}); diff --git a/src/helpers/tests/logger.test.ts b/src/helpers/tests/logger.test.ts new file mode 100644 index 0000000..6f98778 --- /dev/null +++ b/src/helpers/tests/logger.test.ts @@ -0,0 +1,217 @@ +import {Logger, LogLevel} from '../logger'; + +const mockLogger = { + log() {}, + error() {}, + warn() {}, + info() {}, +}; + +describe('logger: log', () => { + const logLevel = LogLevel.Log; + + test('should log log level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + log(str) { + expect(str).toBe('log'); + done(); + }, + }); + + logger.log('log'); + }); + + test('should log info level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + info(str) { + expect(str).toBe('info'); + done(); + }, + }); + + logger.info('info'); + }); + + test('should log warn level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + warn(str) { + expect(str).toBe('warn'); + done(); + }, + }); + + logger.warn('warn'); + }); + + test('should log error level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + error(str) { + expect(str).toBe('error'); + done(); + }, + }); + + logger.error('error'); + }); +}); + +describe('logger: info', () => { + const logLevel = LogLevel.Info; + + test('should not log log level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + log() { + done('Log level has been called'); + }, + }); + + logger.log('log'); + done(); + }); + + test('should log info level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + info(str) { + expect(str).toBe('info'); + done(); + }, + }); + + logger.info('info'); + }); + + test('should log warn level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + warn(str) { + expect(str).toBe('warn'); + done(); + }, + }); + + logger.warn('warn'); + }); + + test('should log error level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + error(str) { + expect(str).toBe('error'); + done(); + }, + }); + + logger.error('error'); + }); +}); + +describe('logger: warn', () => { + const logLevel = LogLevel.Warn; + + test('should not log log level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + log() { + done('Log level has been called'); + }, + }); + + logger.log('log'); + done(); + }); + + test('should not log info level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + info() { + done('Info level has been called'); + }, + }); + + logger.info('info'); + done(); + }); + + test('should log warn level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + warn(str) { + expect(str).toBe('warn'); + done(); + }, + }); + + logger.warn('warn'); + }); + + test('should log error level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + error(str) { + expect(str).toBe('error'); + done(); + }, + }); + + logger.error('error'); + }); +}); + +describe('logger: error', () => { + const logLevel = LogLevel.Error; + + test('should not log log level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + error(str) { + expect(str).toBe('error'); + done(); + }, + }); + + logger.log('log'); + done(); + }); + + test('should not log info level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + info() { + done('Info level has been called'); + }, + }); + + logger.info('info'); + done(); + }); + + test('should not log warn level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + warn() { + done('Warn level has been called'); + }, + }); + + logger.warn('warn'); + done(); + }); + + test('should log error level', done => { + const logger = new Logger(logLevel, { + ...mockLogger, + error(str) { + expect(str).toBe('error'); + done(); + }, + }); + + logger.error('error'); + }); +}); diff --git a/src/helpers/tests/mock-data/address.ts b/src/helpers/tests/mock-data/address.ts new file mode 100644 index 0000000..c707366 --- /dev/null +++ b/src/helpers/tests/mock-data/address.ts @@ -0,0 +1,19 @@ +export const mocked = { + address: 'U6687642817984673870' as const, + passphrase: + 'wrap track hamster grocery casual talk theory half artist toast art essence', + publicKey: Buffer.from( + '7db9b51bc75fed7b8e631e2efaad38305b12c6b3d3d9f6af3498fdcb7b35c284', + 'hex' + ), + privateKey: Buffer.from( + '24a26e6cd6283528f6e2637dcf834434176cf8696647bd4aa6223f349882dc967db9b51bc75fed7b8e631e2efaad38305b12c6b3d3d9f6af3498fdcb7b35c284', + 'hex' + ), + hash: Buffer.from( + '24a26e6cd6283528f6e2637dcf834434176cf8696647bd4aa6223f349882dc96', + 'hex' + ), + recipientId: 'U1234567890123456789' as const, + amount: 99999999999, +}; diff --git a/src/helpers/tests/time.test.js b/src/helpers/tests/time.test.js deleted file mode 100644 index 48319a8..0000000 --- a/src/helpers/tests/time.test.js +++ /dev/null @@ -1,10 +0,0 @@ -const time = require('../time'); -const {epochTime} = require('../constants'); - -describe('getTime', () => { - test('Should return 0 for epoch time', () => { - expect( - time.getTime(epochTime.getTime()), - ).toBe(0); - }); -}); diff --git a/src/helpers/tests/time.test.ts b/src/helpers/tests/time.test.ts new file mode 100644 index 0000000..c723f41 --- /dev/null +++ b/src/helpers/tests/time.test.ts @@ -0,0 +1,7 @@ +import {getEpochTime, EPOCH_TIME} from '../time'; + +describe('getTime', () => { + test('should return 0 for epoch time', () => { + expect(getEpochTime(EPOCH_TIME.getTime())).toBe(0); + }); +}); diff --git a/src/helpers/tests/transactionFormer.test.js b/src/helpers/tests/transactionFormer.test.js deleted file mode 100644 index 2e4e12b..0000000 --- a/src/helpers/tests/transactionFormer.test.js +++ /dev/null @@ -1,171 +0,0 @@ -const transactionFormer = require('../transactionFormer'); -const keys = require('../keys'); -const constants = require('../constants'); - -const passPhrase = keys.createNewPassPhrase(); -const keyPair = keys.createKeypairFromPassPhrase(passPhrase); - -describe('Create send transaction', () => { - const transactionType = constants.transactionTypes.SEND; - - test('Should create base transaction', () => { - const data = { - keyPair, - recipientId: 'U123456', - amount: 1, - }; - - const transaction = transactionFormer.createTransaction(transactionType, data); - - expect(transaction).toMatchObject({ - type: transactionType, - amount: 1, - recipientId: 'U123456', - }); - expect(transaction).toHaveProperty('timestamp'); - expect(transaction).toHaveProperty('senderPublicKey'); - expect(transaction).toHaveProperty('senderId'); - expect(transaction).toHaveProperty('asset'); - expect(transaction).toHaveProperty('signature'); - expect( - typeof transaction.signature, - ).toBe('string'); - }); -}); - -describe('Create vote transaction', () => { - const transactionType = constants.transactionTypes.VOTE; - - test('Should create base transaction', () => { - const data = { - keyPair, - votes: [], - }; - - const transaction = transactionFormer.createTransaction(transactionType, data); - - expect(transaction).toMatchObject({ - type: transactionType, - amount: 0, - }); - expect(transaction).toHaveProperty('timestamp'); - expect(transaction).toHaveProperty('recipientId'); - expect(transaction).toHaveProperty('senderPublicKey'); - expect(transaction).toHaveProperty('senderId'); - expect(transaction).toHaveProperty('asset'); - expect(transaction).toHaveProperty('signature'); - expect( - typeof transaction.signature, - ).toBe('string'); - }); -}); - -describe('Create delegate transaction', () => { - const transactionType = constants.transactionTypes.DELEGATE; - const username = 'admtest'; - - test('Should create base transaction', () => { - const data = { - keyPair, - username, - }; - - const transaction = transactionFormer.createTransaction(transactionType, data); - - expect(transaction).toMatchObject({ - type: transactionType, - amount: 0, - asset: { - delegate: { - username, - }, - }, - }); - expect(transaction).toHaveProperty('timestamp'); - expect(transaction).toHaveProperty('senderPublicKey'); - expect(transaction).toHaveProperty('senderId'); - expect(transaction).toHaveProperty('asset'); - expect(transaction).toHaveProperty('recipientId'); - expect(transaction).toHaveProperty('asset.delegate.publicKey'); - expect(transaction).toHaveProperty('signature'); - expect( - typeof transaction.signature, - ).toBe('string'); - }); -}); - -describe('Create chat transaction', () => { - const transactionType = constants.transactionTypes.CHAT_MESSAGE; - - test('Should create base transaction', () => { - const data = { - keyPair, - amount: 1, - message: 'Hello!', - own_message: null, - message_type: 0, - recipientId: 'U123456', - }; - - const transaction = transactionFormer.createTransaction(transactionType, data); - - expect(transaction).toMatchObject({ - type: transactionType, - recipientId: data.recipientId, - amount: 1, - asset: { - chat: { - message: data.message, - own_message: data.own_message, - type: data.message_type, - }, - }, - }); - expect(transaction).toHaveProperty('timestamp'); - expect(transaction).toHaveProperty('senderPublicKey'); - expect(transaction).toHaveProperty('senderId'); - expect(transaction).toHaveProperty('asset'); - expect(transaction).toHaveProperty('recipientId'); - expect(transaction).toHaveProperty('signature'); - expect( - typeof transaction.signature, - ).toBe('string'); - }); -}); - -describe('Create state transaction', () => { - const transactionType = constants.transactionTypes.STATE; - - test('Should create base transaction', () => { - const data = { - keyPair, - key: 'key', - value: 'value', - }; - - const transaction = transactionFormer.createTransaction(transactionType, data); - - expect(transaction).toMatchObject({ - type: transactionType, - recipientId: null, - amount: 0, - asset: { - state: { - key: data.key, - value: data.value, - type: 0, - }, - }, - }); - expect(transaction).toHaveProperty('timestamp'); - expect(transaction).toHaveProperty('senderPublicKey'); - expect(transaction).toHaveProperty('senderId'); - expect(transaction).toHaveProperty('asset'); - expect(transaction).toHaveProperty('recipientId'); - expect(transaction).toHaveProperty('signature'); - expect( - typeof transaction.signature, - ).toBe('string'); - }); -}); - diff --git a/src/helpers/tests/transactions.test.ts b/src/helpers/tests/transactions.test.ts new file mode 100644 index 0000000..5a77879 --- /dev/null +++ b/src/helpers/tests/transactions.test.ts @@ -0,0 +1,185 @@ +import {TransactionType} from '../constants'; +import { + createChatTransaction, + createDelegateTransaction, + createSendTransaction, + createStateTransaction, + createVoteTransaction, +} from '../transactions/index'; +import {mocked} from './mock-data/address'; + +beforeAll(() => { + jest.useFakeTimers(); + jest.setSystemTime(new Date('2021-02-18')); +}); + +/** + * Expected timestamp accross all the created transactions. + */ +const timestamp = 109234800; + +const keyPair = { + publicKey: mocked.publicKey, + privateKey: mocked.privateKey, +}; + +const {address: senderId} = mocked; +const senderPublicKey = mocked.publicKey.toString('hex'); + +describe('createSendTransaction', () => { + const type = TransactionType.SEND; + + const {recipientId, amount} = mocked; + + test('should create simple send transaction', () => { + const transaction = createSendTransaction({recipientId, amount, keyPair}); + + const expectedSignature = + 'd15cf87edecf808454ac0b7f4d80fc07b72dabe43f7e8ee721f4a208831b18278c9635deb1217610c720c8800daf45b2e4d2dd8ae817111a57b67017424f9502'; + + expect(transaction).toStrictEqual({ + type, + amount, + recipientId, + senderId, + senderPublicKey, + timestamp, + asset: {}, + signature: expectedSignature, + }); + }); +}); + +describe('createVoteTransaction', () => { + const type = TransactionType.VOTE; + + test('should create simple vote transaction', () => { + const votes = [ + '+b3d0c0b99f64d0960324089eb678e90d8bcbb3dd8c73ee748e026f8b9a5b5468', + '-9ef1f6212ae871716cfa2d04e3dc5339e8fe75f89818be21ee1d75004983e2a8', + ]; + + const transaction = createVoteTransaction({keyPair, votes}); + + const expectedSignature = + '53101dc124c0be5d6cafc2116a661884594499ea3fae37c09f4a6514ee4a60523113420e62a5ff001394461f5b72f5fa288cfe314fc05adedf2d367f3d2bd901'; + + expect(transaction).toStrictEqual({ + type, + timestamp, + amount: 0, + senderPublicKey, + senderId, + asset: { + votes, + }, + recipientId: senderId, + signature: expectedSignature, + }); + }); +}); + +describe('createDelegateTransaction', () => { + const type = TransactionType.DELEGATE; + const username = 'admtest'; + + test('should create simple delegate transaction', () => { + const data = { + keyPair, + username, + }; + + const transaction = createDelegateTransaction(data); + + const expectedSignature = + '602ead104c66005c2812a5e96e98506771f8ac5e2f21f002e7acb05a5ee0a3db478f2fec993e6997322dfa85cc6dde6286835e12f5211de84bfc1d3add424e00'; + + expect(transaction).toStrictEqual({ + type, + amount: 0, + timestamp, + senderPublicKey, + senderId, + recipientId: null, + asset: { + delegate: { + username, + publicKey: senderPublicKey, + }, + }, + signature: expectedSignature, + }); + }); +}); + +describe('Create chat transaction', () => { + const type = TransactionType.CHAT_MESSAGE; + + test('should create simple chat transaction', () => { + const {recipientId, amount} = mocked; + + const data = { + keyPair, + amount, + recipientId, + message: 'f96383619244c7e06f39f592b55cc551acc72710', + own_message: 'd0801b9a647fd1469883f918ec616241c79d6f6f7914ddb0', + message_type: 1, + }; + + const transaction = createChatTransaction(data); + + const expectedSignature = + 'd720eb7cc1a1ac863a21b02d2537b283bfa056d81761ca133d07253d4d0bd479c321b8cd766bf20d4f5b2de27f7aae8783449f28a8c8ffbf9a9109fe73500f00'; + + expect(transaction).toStrictEqual({ + type, + recipientId, + timestamp, + amount, + senderPublicKey, + senderId, + asset: { + chat: { + message: data.message, + own_message: data.own_message, + type: data.message_type, + }, + }, + signature: expectedSignature, + }); + }); +}); + +describe('Create state transaction', () => { + const type = TransactionType.STATE; + + test('should create simple state transaction', () => { + const data = { + key: 'key', + value: 'value', + }; + + const transaction = createStateTransaction({...data, keyPair}); + + const expectedSignature = + '8d1bf8673b83eef10324414b0793ee26942e9379a9c38b1578a7c4df68cd922dd8a007bd6c03b5a0b6e28b0ecc4be8154fb72435d783a54dc35ac4614d095a09'; + + expect(transaction).toStrictEqual({ + type, + senderId, + senderPublicKey, + timestamp, + recipientId: null, + amount: 0, + asset: { + state: { + key: data.key, + value: data.value, + type: 0, + }, + }, + signature: expectedSignature, + }); + }); +}); diff --git a/src/helpers/tests/validator.test.js b/src/helpers/tests/validator.test.js deleted file mode 100644 index e9d211f..0000000 --- a/src/helpers/tests/validator.test.js +++ /dev/null @@ -1,321 +0,0 @@ -const validator = require('../validator'); - -describe('isNumeric', () => { - test('Should return false for a number', () => { - expect(validator.isNumeric(3)).toBe(false); - }); - - test('Should return false for Infinity', () => { - expect(validator.isNumeric(Infinity)).toBe(false); - }); - - test('Should return false for an object', () => { - expect(validator.isNumeric({})).toBe(false); - }); - - test('Should return false for undefined', () => { - expect(validator.isNumeric(undefined)).toBe(false); - }); - - test('Should return false for NaN', () => { - expect(validator.isNumeric(undefined)).toBe(false); - }); - - test('Should return false for `n3.14`', () => { - expect(validator.isNumeric('n3.14')).toBe(false); - }); - - test('Should return false for `3,14`', () => { - expect(validator.isNumeric('3,14')).toBe(true); - }); - - test('Should return true for `3.14`', () => { - expect(validator.isNumeric('3.14')).toBe(true); - }); - - test('Should return true for ` 3.14`', () => { - expect(validator.isNumeric(' 3.14')).toBe(true); - }); -}); - -describe('validatePassPhrase', () => { - test('Should return false for a number', () => { - expect(validator.validatePassPhrase(3)).toBe(false); - }); - - test('Should return false for an object', () => { - expect(validator.validatePassPhrase({})).toBe(false); - }); - - test('Should return false for undefined', () => { - expect(validator.validatePassPhrase(undefined)).toBe(false); - }); - - test('Should return false for NaN', () => { - expect(validator.validatePassPhrase(undefined)).toBe(false); - }); - - test('Should return false for a too short string', () => { - expect(validator.validatePassPhrase('short')).toBe(false); - }); - - test('Should return true for a long string', () => { - expect(validator.validatePassPhrase('word '.repeat(12))).toBe(true); - }); -}); - -describe('validateAdmAddress', () => { - test('Should return false for a number', () => { - expect(validator.validateAdmAddress(3)).toBe(false); - }); - - test('Should return false for an object', () => { - expect(validator.validateAdmAddress({})).toBe(false); - }); - - test('Should return false for undefined', () => { - expect(validator.validateAdmAddress(undefined)).toBe(false); - }); - - test('Should return false for NaN', () => { - expect(validator.validateAdmAddress(undefined)).toBe(false); - }); - - test('Should return false for U123', () => { - expect(validator.validateAdmAddress('U123')).toBe(false); - }); - - test('Should return false for ` U123456`', () => { - expect(validator.validateAdmAddress(' U123456')).toBe(false); - }); - - test('Should return false for `U123213N123`', () => { - expect(validator.validateAdmAddress('U123213N123')).toBe(false); - }); - - test('Should return true for U123456', () => { - expect(validator.validateAdmAddress('U1234506')).toBe(true); - }); - - test('Should return true for U01234561293812931283918239', () => { - expect(validator.validateAdmAddress('U01234561293812931283918239')).toBe(true); - }); -}); - -describe('validateAdmPublicKey', () => { - test('Should return false for a number', () => { - expect(validator.validateAdmPublicKey(3)).toBe(false); - }); - - test('Should return false for an object', () => { - expect(validator.validateAdmPublicKey({})).toBe(false); - }); - - test('Should return false for undefined', () => { - expect(validator.validateAdmPublicKey(undefined)).toBe(false); - }); - - test('Should return false for NaN', () => { - expect(validator.validateAdmPublicKey(undefined)).toBe(false); - }); - - test('Should return false for a short string', () => { - expect(validator.validateAdmPublicKey('0f')).toBe(false); - }); - - test('Should return false for a string that contains `L`', () => { - expect(validator.validateAdmPublicKey('Le003f782cd1c1c84a6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432')).toBe(false); - }); - - test('Should return true for a public key that starts with a number', () => { - expect(validator.validateAdmPublicKey('4e003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432')).toBe(true); - }); - - test('Should return true for a public key that starts with a letter', () => { - expect(validator.validateAdmPublicKey('e4003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432')).toBe(true); - }); -}); - -describe('validateAdmVoteForAddress', () => { - test('Should return false for a number', () => { - expect(validator.validateAdmVoteForAddress(3)).toBe(false); - }); - - test('Should return false for an object', () => { - expect(validator.validateAdmVoteForAddress({})).toBe(false); - }); - - test('Should return false for undefined', () => { - expect(validator.validateAdmVoteForAddress(undefined)).toBe(false); - }); - - test('Should return false for NaN', () => { - expect(validator.validateAdmVoteForAddress(undefined)).toBe(false); - }); - - test('Should return false for a short string', () => { - expect(validator.validateAdmVoteForAddress('0f')).toBe(false); - }); - - test('Should return false for a string that starts with `L`', () => { - expect(validator.validateAdmVoteForAddress('L01234561293812931283918239')).toBe(false); - }); - - test('Should return false for an address that starts with a number', () => { - expect(validator.validateAdmVoteForAddress('0U1234561293812931283918239')).toBe(false); - }); - - test('Should return false for an address that starts with a letter', () => { - expect(validator.validateAdmVoteForAddress('U01234561293812931283918239')).toBe(false); - }); - - test('Should return true for an address with a plus', () => { - expect(validator.validateAdmVoteForAddress('+U01234561293812931283918239')).toBe(true); - }); - - test('Should return true for an address with a minus', () => { - expect(validator.validateAdmVoteForAddress('+U01234561293812931283918239')).toBe(true); - }); -}); - -describe('validateAdmVoteForPublicKey', () => { - test('Should return false for a number', () => { - expect(validator.validateAdmVoteForPublicKey(3)).toBe(false); - }); - - test('Should return false for an object', () => { - expect(validator.validateAdmVoteForPublicKey({})).toBe(false); - }); - - test('Should return false for undefined', () => { - expect(validator.validateAdmVoteForPublicKey(undefined)).toBe(false); - }); - - test('Should return false for NaN', () => { - expect(validator.validateAdmVoteForPublicKey(undefined)).toBe(false); - }); - - test('Should return false for a short string', () => { - expect(validator.validateAdmVoteForPublicKey('0f')).toBe(false); - }); - - test('Should return false for a string that starts with `L`', () => { - expect(validator.validateAdmVoteForPublicKey('+L4e003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432')).toBe(false); - }); - - test('Should return false for a public key that starts with a number', () => { - expect(validator.validateAdmVoteForPublicKey('4e003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432')).toBe(false); - }); - - test('Should return false for a public key that starts with a letter', () => { - expect(validator.validateAdmVoteForPublicKey('e4003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432')).toBe(false); - }); - - test('Should return true for a public key with a plus', () => { - expect(validator.validateAdmVoteForPublicKey('+4e003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432')).toBe(true); - }); - - test('Should return true for a public key with a minus', () => { - expect(validator.validateAdmVoteForPublicKey('+4e003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432')).toBe(true); - }); -}); - -describe('validateAdmVoteForDelegateName', () => { - test('Should return false for a number', () => { - expect(validator.validateAdmVoteForDelegateName(3)).toBe(false); - }); - - test('Should return false for an object', () => { - expect(validator.validateAdmVoteForDelegateName({})).toBe(false); - }); - - test('Should return false for undefined', () => { - expect(validator.validateAdmVoteForDelegateName(undefined)).toBe(false); - }); - - test('Should return false for NaN', () => { - expect(validator.validateAdmVoteForDelegateName(undefined)).toBe(false); - }); - - test('Should return false for a short string', () => { - expect(validator.validateAdmVoteForDelegateName('0f')).toBe(false); - }); - - test('Should return false for a vote without delegate name', () => { - expect(validator.validateAdmVoteForDelegateName('+')).toBe(false); - }); - - test('Should return false for a too long delegate name', () => { - expect(validator.validateAdmVoteForDelegateName('+e003f782cd1c1c84A6767a871321af2e')).toBe(false); - }); - - test('Should return false for a vote that starts with a number', () => { - expect(validator.validateAdmVoteForDelegateName('4darksinc')).toBe(false); - }); - - test('Should return false for a vote that starts with a letter', () => { - expect(validator.validateAdmVoteForDelegateName('darksinc')).toBe(false); - }); - - test('Should return true for a delegate name with a plus', () => { - expect(validator.validateAdmVoteForDelegateName('+darksinc')).toBe(true); - }); - - test('Should return true for a delegate name with a minus', () => { - expect(validator.validateAdmVoteForDelegateName('+darksinc')).toBe(true); - }); -}); - -describe('validateMessage', () => { - test('Result should be false for a number message', () => { - expect(validator.validateMessage(3).result).toBe(false); - }); - - test('Result should be false for an object message', () => { - expect(validator.validateMessage({}).result).toBe(false); - }); - - test('Result should be true for a string message', () => { - expect(validator.validateMessage('').result).toBe(true); - }); - - test('Result should be false for a string rich message', () => { - expect(validator.validateMessage('', 2).result).toBe(false); - }); - - test('Result should be false for a string signal message', () => { - expect(validator.validateMessage('', 3).result).toBe(false); - }); - - test('Result should be false for an empty json rich message', () => { - expect(validator.validateMessage('{}', 2).result).toBe(false); - }); - - test('Result should be false for an empty json signal message', () => { - expect(validator.validateMessage('{}', 3).result).toBe(false); - }); - - test('Result should be true for a json rich message with the given amount', () => { - expect(validator.validateMessage('{"amount": "0.13"}', 2).result).toBe(true); - }); - - test('Result should be true for a json signal message with the given amount', () => { - expect(validator.validateMessage('{"amount": "0.13"}', 3).result).toBe(true); - }); - - test('Result should be false for a json rich message with upercase coin name', () => { - expect(validator.validateMessage('{"amount": "0.13", "type": "ETH_transaction"}', 2).result).toBe(false); - }); - - test('Result should be false for a json signal message with upercase coin name', () => { - expect(validator.validateMessage('{"amount": "0.13", "type": "ETH_transaction"}', 3).result).toBe(false); - }); - - test('Result should be true for a json rich message with lowercase coin name', () => { - expect(validator.validateMessage('{"amount": "0.13", "type": "eth_transaction"}', 2).result).toBe(true); - }); - - test('Result should be true for a json signal message with lowercase coin name', () => { - expect(validator.validateMessage('{"amount": "0.13", "type": "eth_transaction"}', 3).result).toBe(true); - }); -}); diff --git a/src/helpers/tests/validator.test.ts b/src/helpers/tests/validator.test.ts new file mode 100644 index 0000000..8465095 --- /dev/null +++ b/src/helpers/tests/validator.test.ts @@ -0,0 +1,435 @@ +import * as validator from '../validator'; + +describe('isNumeric', () => { + test('should return false for a number', () => { + expect(validator.isNumeric(3)).toBe(false); + }); + + test('should return false for Infinity', () => { + expect(validator.isNumeric(Infinity)).toBe(false); + }); + + test('should return false for an object', () => { + expect(validator.isNumeric({})).toBe(false); + }); + + test('should return false for undefined', () => { + expect(validator.isNumeric(undefined)).toBe(false); + }); + + test('should return false for NaN', () => { + expect(validator.isNumeric(NaN)).toBe(false); + }); + + test('should return false for `n3.14`', () => { + expect(validator.isNumeric('n3.14')).toBe(false); + }); + + test('should return true for `3,14`', () => { + expect(validator.isNumeric('3,14')).toBe(true); + }); + + test('should return true for `3.14`', () => { + expect(validator.isNumeric('3.14')).toBe(true); + }); + + test('should return true for ` 3.14`', () => { + expect(validator.isNumeric(' 3.14')).toBe(true); + }); +}); + +describe('isPassphrase', () => { + test('should return false for a number', () => { + expect(validator.isPassphrase(3)).toBe(false); + }); + + test('should return false for an object', () => { + expect(validator.isPassphrase({})).toBe(false); + }); + + test('should return false for undefined', () => { + expect(validator.isPassphrase(undefined)).toBe(false); + }); + + test('should return false for NaN', () => { + expect(validator.isPassphrase(NaN)).toBe(false); + }); + + test('should return false for a too short string', () => { + expect(validator.isPassphrase('short')).toBe(false); + }); + + test('should return true for a 12 word string', () => { + expect(validator.isPassphrase('word '.repeat(12))).toBe(true); + }); +}); + +describe('isAdmAddress', () => { + test('should return false for a number', () => { + expect(validator.isAdmAddress(3)).toBe(false); + }); + + test('should return false for an object', () => { + expect(validator.isAdmAddress({})).toBe(false); + }); + + test('should return false for undefined', () => { + expect(validator.isAdmAddress(undefined)).toBe(false); + }); + + test('should return false for NaN', () => { + expect(validator.isAdmAddress(NaN)).toBe(false); + }); + + test('should return false for U123', () => { + expect(validator.isAdmAddress('U123')).toBe(false); + }); + + test('should return false for ` U123456`', () => { + expect(validator.isAdmAddress(' U123456')).toBe(false); + }); + + test('should return false for `U123213N123`', () => { + expect(validator.isAdmAddress('U123213N123')).toBe(false); + }); + + test('should return true for U123456', () => { + expect(validator.isAdmAddress('U1234506')).toBe(true); + }); + + test('should return true for U01234561293812931283918239', () => { + expect(validator.isAdmAddress('U01234561293812931283918239')).toBe(true); + }); +}); + +describe('isAdmPublicKey', () => { + test('should return false for a number', () => { + expect(validator.isAdmPublicKey(3)).toBe(false); + }); + + test('should return false for an object', () => { + expect(validator.isAdmPublicKey({})).toBe(false); + }); + + test('should return false for undefined', () => { + expect(validator.isAdmPublicKey(undefined)).toBe(false); + }); + + test('should return false for NaN', () => { + expect(validator.isAdmPublicKey(NaN)).toBe(false); + }); + + test('should return false for a short string', () => { + expect(validator.isAdmPublicKey('0f')).toBe(false); + }); + + test('should return false for a string that contains `L`', () => { + expect( + validator.isAdmPublicKey( + 'Le003f782cd1c1c84a6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432' + ) + ).toBe(false); + }); + + test('should return true for a public key that starts with a number', () => { + expect( + validator.isAdmPublicKey( + '4e003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432' + ) + ).toBe(true); + }); + + test('should return true for a public key that starts with a letter', () => { + expect( + validator.isAdmPublicKey( + 'e4003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432' + ) + ).toBe(true); + }); +}); + +describe('isAdmVoteForAddress', () => { + test('should return false for a number', () => { + expect(validator.isAdmVoteForAddress(3)).toBe(false); + }); + + test('should return false for an object', () => { + expect(validator.isAdmVoteForAddress({})).toBe(false); + }); + + test('should return false for undefined', () => { + expect(validator.isAdmVoteForAddress(undefined)).toBe(false); + }); + + test('should return false for NaN', () => { + expect(validator.isAdmVoteForAddress(NaN)).toBe(false); + }); + + test('should return false for a short string', () => { + expect(validator.isAdmVoteForAddress('0f')).toBe(false); + }); + + test('should return false for a string that starts with `L`', () => { + expect(validator.isAdmVoteForAddress('L01234561293812931283918239')).toBe( + false + ); + }); + + test('should return false for an address that starts with a number', () => { + expect(validator.isAdmVoteForAddress('0U1234561293812931283918239')).toBe( + false + ); + }); + + test('should return false for an address that starts with a letter', () => { + expect(validator.isAdmVoteForAddress('U01234561293812931283918239')).toBe( + false + ); + }); + + test('should return true for an address with a plus', () => { + expect(validator.isAdmVoteForAddress('+U01234561293812931283918239')).toBe( + true + ); + }); + + test('should return true for an address with a minus', () => { + expect(validator.isAdmVoteForAddress('+U01234561293812931283918239')).toBe( + true + ); + }); +}); + +describe('isAdmVoteForPublicKey', () => { + test('should return false for a number', () => { + expect(validator.isAdmVoteForPublicKey(3)).toBe(false); + }); + + test('should return false for an object', () => { + expect(validator.isAdmVoteForPublicKey({})).toBe(false); + }); + + test('should return false for undefined', () => { + expect(validator.isAdmVoteForPublicKey(undefined)).toBe(false); + }); + + test('should return false for NaN', () => { + expect(validator.isAdmVoteForPublicKey(NaN)).toBe(false); + }); + + test('should return false for a short string', () => { + expect(validator.isAdmVoteForPublicKey('0f')).toBe(false); + }); + + test('should return false for a string that starts with `L`', () => { + expect( + validator.isAdmVoteForPublicKey( + '+L4e003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432' + ) + ).toBe(false); + }); + + test('should return false for a public key that starts with a number', () => { + expect( + validator.isAdmVoteForPublicKey( + '4e003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432' + ) + ).toBe(false); + }); + + test('should return false for a public key that starts with a letter', () => { + expect( + validator.isAdmVoteForPublicKey( + 'e4003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432' + ) + ).toBe(false); + }); + + test('should return true for a public key with a plus', () => { + expect( + validator.isAdmVoteForPublicKey( + '+4e003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432' + ) + ).toBe(true); + }); + + test('should return true for a public key with a minus', () => { + expect( + validator.isAdmVoteForPublicKey( + '+4e003f782cd1c1c84A6767a871321af2ecdb3da8d8f6b8d1f13179835b6ec432' + ) + ).toBe(true); + }); +}); + +describe('isAdmVoteForDelegateName', () => { + test('should return false for a number', () => { + expect(validator.isAdmVoteForDelegateName(3)).toBe(false); + }); + + test('should return false for an object', () => { + expect(validator.isAdmVoteForDelegateName({})).toBe(false); + }); + + test('should return false for undefined', () => { + expect(validator.isAdmVoteForDelegateName(undefined)).toBe(false); + }); + + test('should return false for NaN', () => { + expect(validator.isAdmVoteForDelegateName(NaN)).toBe(false); + }); + + test('should return false for a short string', () => { + expect(validator.isAdmVoteForDelegateName('0f')).toBe(false); + }); + + test('should return false for a vote without delegate name', () => { + expect(validator.isAdmVoteForDelegateName('+')).toBe(false); + }); + + test('should return false for a too long delegate name', () => { + expect( + validator.isAdmVoteForDelegateName('+e003f782cd1c1c84A6767a871321af2e') + ).toBe(false); + }); + + test('should return false for a vote that starts with a number', () => { + expect(validator.isAdmVoteForDelegateName('4darksinc')).toBe(false); + }); + + test('should return false for a vote that starts with a letter', () => { + expect(validator.isAdmVoteForDelegateName('darksinc')).toBe(false); + }); + + test('should return true for a delegate name with a plus', () => { + expect(validator.isAdmVoteForDelegateName('+darksinc')).toBe(true); + }); + + test('should return true for a delegate name with a minus', () => { + expect(validator.isAdmVoteForDelegateName('+darksinc')).toBe(true); + }); +}); + +describe('validateMessage', () => { + test('should return false for a number message', () => { + expect(validator.validateMessage(3 as any).success).toBe(false); + }); + + test('should return false for an object message', () => { + expect(validator.validateMessage({} as any).success).toBe(false); + }); + + test('should return true for an empty string message', () => { + expect(validator.validateMessage('').success).toBe(true); + }); + + test('should return false for an empty string rich message', () => { + expect(validator.validateMessage('', 2).success).toBe(false); + }); + + test('should return false for an empty string signal message', () => { + expect(validator.validateMessage('', 3).success).toBe(false); + }); + + test('should return false for an empty json rich message', () => { + expect(validator.validateMessage('{}', 2).success).toBe(false); + }); + + test('should return false for an empty json signal message', () => { + expect(validator.validateMessage('{}', 3).success).toBe(false); + }); + + test('should return true for a json rich message with the given amount', () => { + expect(validator.validateMessage('{"amount": "0.13"}', 2).success).toBe( + true + ); + }); + + test('should return true for a json signal message with the given amount', () => { + expect(validator.validateMessage('{"amount": "0.13"}', 3).success).toBe( + true + ); + }); + + test('should return false for a json rich message with upercase coin name', () => { + expect( + validator.validateMessage( + '{"amount": "0.13", "type": "ETH_transaction"}', + 2 + ).success + ).toBe(false); + }); + + test('should return false for a json signal message with upercase coin name', () => { + expect( + validator.validateMessage( + '{"amount": "0.13", "type": "ETH_transaction"}', + 3 + ).success + ).toBe(false); + }); + + test('should return true for a json rich message with lowercase coin name', () => { + expect( + validator.validateMessage( + '{"amount": "0.13", "type": "eth_transaction"}', + 2 + ).success + ).toBe(true); + }); + + test('should return true for a json signal message with lowercase coin name', () => { + expect( + validator.validateMessage( + '{"amount": "0.13", "type": "eth_transaction"}', + 3 + ).success + ).toBe(true); + }); +}); + +describe('isDelegateName', () => { + test('should return false for a number', () => { + expect(validator.isDelegateName(1)).toBe(false); + }); + + test('should return true for valid name', () => { + expect(validator.isDelegateName('validname')).toBe(true); + }); + + test('should return true for name with numbers', () => { + expect(validator.isDelegateName('name_with1_number')).toBe(true); + }); + + test('should return true for name with special characters', () => { + expect(validator.isDelegateName('allow&special!chars')).toBe(true); + }); + + test('should return true for short name (minimum length)', () => { + expect(validator.isDelegateName('short')).toBe(true); + }); + + test('should return true for name with underscores', () => { + expect(validator.isDelegateName('this_is_a_valid_name')).toBe(true); + }); + + test('should return false for an empty string', () => { + expect(validator.isDelegateName('')).toBe(false); + }); + + test('should return false for camel case with spaces', () => { + expect(validator.isDelegateName('CamelCase')).toBe(false); + }); + + test('should return false for long name (exceeds maximum length)', () => { + expect(validator.isDelegateName('name-that-is-way-too-long')).toBe(false); + }); + + test('should return false for name with invalid characters', () => { + expect(validator.isDelegateName('!invalid$%characters&')).toBe(false); + }); + + test('should return false for name with spaces', () => { + expect(validator.isDelegateName('there is space')).toBe(false); + }); +}); diff --git a/src/helpers/time.js b/src/helpers/time.js deleted file mode 100644 index 4504cac..0000000 --- a/src/helpers/time.js +++ /dev/null @@ -1,15 +0,0 @@ -const constants = require('./constants.js'); - -module.exports = { - getEpochTime(time) { - const startTime = time ?? Date.now(); - - const {epochTime} = constants; - const epochTimeMs = epochTime.getTime(); - - return Math.floor((startTime - epochTimeMs) / 1000); - }, - getTime(time) { - return this.getEpochTime(time); - }, -}; diff --git a/src/helpers/time.ts b/src/helpers/time.ts new file mode 100644 index 0000000..3a3e417 --- /dev/null +++ b/src/helpers/time.ts @@ -0,0 +1,10 @@ +export const EPOCH_TIME = new Date(Date.UTC(2017, 8, 2, 17, 0, 0, 0)); + +export const getEpochTime = (timestamp?: number) => { + const startTimestamp = timestamp ?? Date.now(); + const epochTimestamp = EPOCH_TIME.getTime(); + + return Math.floor((startTimestamp - epochTimestamp) / 1000); +}; + +export const unixTimestamp = () => new Date().getTime(); diff --git a/src/helpers/transactionFormer.js b/src/helpers/transactionFormer.js deleted file mode 100644 index e03cd91..0000000 --- a/src/helpers/transactionFormer.js +++ /dev/null @@ -1,340 +0,0 @@ -const sodium = require('sodium-browserify-tweetnacl'); -const crypto = require('crypto'); -const ByteBuffer = require('bytebuffer'); - -const BigNum = require('./bignumber.js'); -const keys = require('./keys.js'); -const constants = require('./constants.js'); -const time = require('./time.js'); - -module.exports = { - createTransaction(type, data) { - const { - SEND, - VOTE, - DELEGATE, - CHAT_MESSAGE, - STATE, - } = constants.transactionTypes; - - const actions = { - [SEND]: () => this.createSendTransaction(data), - [VOTE]: () => this.createVoteTransaction(data), - [DELEGATE]: () => this.createDelegateTransaction(data), - [CHAT_MESSAGE]: () => this.createChatTransaction(data), - [STATE]: () => this.createStateTransaction(data), - }; - - const action = actions[type]; - - return action ? action() : ({}); - }, - createBasicTransaction(data) { - const transaction = { - type: data.transactionType, - timestamp: time.getTime(), - amount: 0, - senderPublicKey: data.keyPair.publicKey.toString('hex'), - senderId: keys.createAddressFromPublicKey(data.keyPair.publicKey), - asset: {}, - }; - - return transaction; - }, - createSendTransaction(data) { - const details = { - ...data, - transactionType: constants.transactionTypes.SEND, - }; - - const transaction = { - ...this.createBasicTransaction(details), - recipientId: details.recipientId, - amount: details.amount, - asset: {}, - }; - - transaction.signature = this.transactionSign(transaction, details.keyPair); - - return transaction; - }, - createStateTransaction(data) { - const details = { - ...data, - transactionType: constants.transactionTypes.STATE, - }; - - const transaction = { - ...this.createBasicTransaction(details), - recipientId: null, - asset: { - state: { - key: details.key, - value: details.value, - type: 0, - }, - }, - }; - - transaction.signature = this.transactionSign(transaction, details.keyPair); - - return transaction; - }, - createChatTransaction(data) { - const details = { - ...data, - transactionType: constants.transactionTypes.CHAT_MESSAGE, - }; - - const transaction = { - ...this.createBasicTransaction(details), - recipientId: details.recipientId, - amount: details.amount || 0, - asset: { - chat: { - message: data.message, - own_message: data.own_message, - type: data.message_type, - }, - }, - }; - - transaction.signature = this.transactionSign(transaction, details.keyPair); - - return transaction; - }, - createDelegateTransaction(data) { - const details = { - ...data, - transactionType: constants.transactionTypes.DELEGATE, - }; - - const transaction = { - ...this.createBasicTransaction(details), - recipientId: null, - asset: { - delegate: { - username: details.username, - publicKey: details.keyPair.publicKey.toString('hex'), - }, - }, - }; - - transaction.signature = this.transactionSign(transaction, details.keyPair); - - return transaction; - }, - createVoteTransaction(data) { - const details = { - ...data, - transactionType: constants.transactionTypes.VOTE, - }; - - const transaction = { - ...this.createBasicTransaction(details), - asset: { - votes: details.votes, - }, - }; - - transaction.recipientId = transaction.senderId; - transaction.signature = this.transactionSign(transaction, details.keyPair); - - return transaction; - }, - getHash(trs) { - const hash = crypto - .createHash('sha256') - .update(this.getBytes(trs)) - .digest(); - - return hash; - }, - getAssetBytes(transaction) { - const {type} = transaction; - const { - SEND, - VOTE, - DELEGATE, - CHAT_MESSAGE, - STATE, - } = constants.transactionTypes; - - if (type === SEND) { - return {assetBytes: null, assetSize: 0}; - } - - const actions = { - [VOTE]: this.voteGetBytes, - [DELEGATE]: this.delegatesGetBytes, - [CHAT_MESSAGE]: this.chatGetBytes, - [STATE]: this.statesGetBytes, - }; - - const getBytes = actions[type]; - - if (typeof getBytes === 'function') { - const assetBytes = getBytes(transaction); - - return {assetBytes, assetSize: assetBytes.length}; - } - }, - getBytes(transaction) { - const skipSignature = false; - const skipSecondSignature = true; - - const {assetSize, assetBytes} = this.getAssetBytes(transaction); - - const bb = new ByteBuffer(1 + 4 + 32 + 8 + 8 + 64 + 64 + assetSize, true); - - bb.writeByte(transaction.type); - bb.writeInt(transaction.timestamp); - - const senderPublicKeyBuffer = Buffer.from(transaction.senderPublicKey, 'hex'); - - for (const buf of senderPublicKeyBuffer) { - bb.writeByte(buf); - } - - if (transaction.requesterPublicKey) { - const requesterPublicKey = Buffer.from(transaction.requesterPublicKey, 'hex'); - - for (const buf of requesterPublicKey) { - bb.writeByte(buf); - } - } - - if (transaction.recipientId) { - const recipient = new BigNum( - transaction.recipientId.slice(1), - ).toBuffer({size: 8}); - - for (let i = 0; i < 8; i++) { - bb.writeByte(recipient[i] || 0); - } - } else { - for (let i = 0; i < 8; i++) { - bb.writeByte(0); - } - } - - bb.writeLong(transaction.amount); - - if (assetSize > 0) { - for (const assetByte of assetBytes) { - bb.writeByte(assetByte); - } - } - - if (!skipSignature && transaction.signature) { - const signatureBuffer = Buffer.from(transaction.signature, 'hex'); - - for (const buf of signatureBuffer) { - bb.writeByte(buf); - } - } - - if (!skipSecondSignature && transaction.signSignature) { - const signSignatureBuffer = Buffer.from(transaction.signSignature, 'hex'); - - for (const buf of signSignatureBuffer) { - bb.writeByte(buf); - } - } - - bb.flip(); - - const arrayBuffer = new Uint8Array(bb.toArrayBuffer()); - const buffer = []; - - for (let i = 0; i < arrayBuffer.length; i++) { - buffer[i] = arrayBuffer[i]; - } - - return Buffer.from(buffer); - }, - transactionSign(trs, keypair) { - const hash = this.getHash(trs); - - return this.sign(hash, keypair).toString('hex'); - }, - voteGetBytes(trs) { - const {votes} = trs.asset; - - const buf = votes ? - Buffer.from(votes.join(''), 'utf8') : - null; - - return buf; - }, - delegatesGetBytes(trs) { - const {username} = trs.asset.delegate; - - const buf = username ? - Buffer.from(username, 'utf8') : - null; - - return buf; - }, - statesGetBytes(trs) { - const {value} = trs.asset.state; - - if (!value) { - return null; - } - - let buf = Buffer.from([]); - - const {key, type} = trs.asset.state; - - const stateBuf = Buffer.from(value); - - buf = Buffer.concat([buf, stateBuf]); - - if (key) { - const keyBuf = Buffer.from(key); - buf = Buffer.concat([buf, keyBuf]); - } - - const bb = new ByteBuffer(4 + 4, true); - - bb.writeInt(type); - bb.flip(); - - buf = Buffer.concat([buf, bb.toBuffer()]); - - return buf; - }, - chatGetBytes(trs) { - let buf = Buffer.from([]); - - const {message} = trs.asset.chat; - const messageBuf = Buffer.from(message, 'hex'); - - buf = Buffer.concat([buf, messageBuf]); - - const {own_message: ownMessage} = trs.asset.chat; - - if (ownMessage) { - const ownMessageBuf = Buffer.from(ownMessage, 'hex'); - buf = Buffer.concat([buf, ownMessageBuf]); - } - - const bb = new ByteBuffer(4 + 4, true); - - bb.writeInt(trs.asset.chat.type); - bb.flip(); - - buf = Buffer.concat([buf, Buffer.from(bb.toBuffer())]); - - return buf; - }, - sign(hash, keypair) { - const sign = sodium.crypto_sign_detached( - hash, - Buffer.from(keypair.privateKey, 'hex'), - ); - - return sign; - }, -}; diff --git a/src/helpers/transactions/hash.ts b/src/helpers/transactions/hash.ts new file mode 100644 index 0000000..ec20048 --- /dev/null +++ b/src/helpers/transactions/hash.ts @@ -0,0 +1,258 @@ +import sodium from 'sodium-browserify-tweetnacl'; +import ByteBuffer from 'bytebuffer'; +import crypto from 'crypto'; + +import BigNumber from 'bignumber.js'; +import {toBuffer} from '../bignumber'; + +import {MessageType, TransactionType} from '../constants'; +import {KeyPair} from '../keys'; + +export interface BasicTransaction { + timestamp: number; + amount: number; + senderPublicKey: string; + senderId: string; + asset: {}; +} + +export interface SendTransaction extends BasicTransaction { + type: 0; + recipientId: string; + amount: number; + asset: {}; +} + +export interface StateTransaction extends BasicTransaction { + type: 9; + recipientId: null; + asset: { + state: { + key: string; + value: string; + type: 0; + }; + }; +} + +export interface ChatTransaction extends BasicTransaction { + type: 8; + recipientId: string; + amount: number; + asset: { + chat: { + message: string; + own_message: string; + type: MessageType; + }; + }; +} + +export interface DelegateTransaction extends BasicTransaction { + type: 2; + recipientId: null; + asset: { + delegate: { + username: string; + publicKey: string; + }; + }; +} + +export interface VoteTransaction extends BasicTransaction { + type: 3; + asset: { + votes: string[]; + }; +} + +export type SomeTransaction = + | VoteTransaction + | DelegateTransaction + | ChatTransaction + | StateTransaction + | SendTransaction; + +export type PossiblySignedTransaction = SomeTransaction & { + signature?: string; +}; + +export type SignedTransaction = SomeTransaction & { + signature: string; +}; + +export function getHash( + trs: PossiblySignedTransaction, + options = {skipSignature: true} +) { + const hash = crypto + .createHash('sha256') + .update(getBytes(trs, options.skipSignature)) + .digest(); + + return hash; +} + +export function getAssetBytes(transaction: PossiblySignedTransaction) { + const {type} = transaction; + const {VOTE, DELEGATE, CHAT_MESSAGE, STATE} = TransactionType; + + let assetBytes = null; + + if (type === VOTE) { + assetBytes = voteGetBytes(transaction); + } else if (type === DELEGATE) { + assetBytes = delegatesGetBytes(transaction); + } else if (type === CHAT_MESSAGE) { + assetBytes = chatGetBytes(transaction); + } else if (type === STATE) { + assetBytes = statesGetBytes(transaction); + } + + return {assetBytes, assetSize: assetBytes?.length || 0}; +} + +export function getBytes( + transaction: PossiblySignedTransaction, + skipSignature = true +) { + const result = getAssetBytes(transaction); + + if (!result) { + throw new Error('Not supported transaction type'); + } + + const {assetSize, assetBytes} = result; + + const bb = new ByteBuffer(1 + 4 + 32 + 8 + 8 + 64 + 64 + assetSize, true); + + bb.writeByte(transaction.type); + bb.writeInt(transaction.timestamp); + + const senderPublicKeyBuffer = Buffer.from(transaction.senderPublicKey, 'hex'); + + for (const buf of senderPublicKeyBuffer) { + bb.writeByte(buf); + } + + if ('recipientId' in transaction && transaction.recipientId) { + const bignum = new BigNumber(transaction.recipientId.slice(1)); + const recipient = toBuffer(bignum, {size: 8}); + + for (let i = 0; i < 8; i++) { + bb.writeByte(recipient[i] || 0); + } + } else { + for (let i = 0; i < 8; i++) { + bb.writeByte(0); + } + } + + bb.writeLong(transaction.amount); + + if (assetBytes && assetSize > 0) { + for (const assetByte of assetBytes) { + bb.writeByte(assetByte); + } + } + + if (!skipSignature && transaction.signature) { + const signatureBuffer = Buffer.from(transaction.signature, 'hex'); + for (let i = 0; i < signatureBuffer.length; i++) { + bb.writeByte(signatureBuffer[i]); + } + } + + bb.flip(); + + const arrayBuffer = new Uint8Array(bb.toArrayBuffer()); + const buffer: number[] = []; + + for (let i = 0; i < arrayBuffer.length; i++) { + buffer[i] = arrayBuffer[i]; + } + + return Buffer.from(buffer); +} + +export function signTransaction(trs: SomeTransaction, keypair: KeyPair) { + const hash = getHash(trs); + + return sign(hash, keypair).toString('hex'); +} + +export function voteGetBytes(trs: VoteTransaction) { + const {votes} = trs.asset; + + return votes ? Buffer.from(votes.join(''), 'utf8') : null; +} + +export function delegatesGetBytes(trs: DelegateTransaction) { + const {username} = trs.asset.delegate; + + return username ? Buffer.from(username, 'utf8') : null; +} + +export function statesGetBytes(trs: StateTransaction) { + const {value} = trs.asset.state; + + if (!value) { + return null; + } + + let buf = Buffer.from([]); + + const {key, type} = trs.asset.state; + + const stateBuf = Buffer.from(value); + + buf = Buffer.concat([buf, stateBuf]); + + if (key) { + const keyBuf = Buffer.from(key); + buf = Buffer.concat([buf, keyBuf]); + } + + const bb = new ByteBuffer(4 + 4, true); + + bb.writeInt(type); + bb.flip(); + + buf = Buffer.concat([buf, bb.toBuffer()]); + + return buf; +} + +export function chatGetBytes(trs: ChatTransaction) { + let buf = Buffer.from([]); + + const {message} = trs.asset.chat; + const messageBuf = Buffer.from(message, 'hex'); + + buf = Buffer.concat([buf, messageBuf]); + + const {own_message: ownMessage} = trs.asset.chat; + + if (ownMessage) { + const ownMessageBuf = Buffer.from(ownMessage, 'hex'); + buf = Buffer.concat([buf, ownMessageBuf]); + } + + const bb = new ByteBuffer(4 + 4, true); + + bb.writeInt(trs.asset.chat.type); + bb.flip(); + + buf = Buffer.concat([buf, Buffer.from(bb.toBuffer())]); + + return buf; +} + +export function sign(hash: Buffer, keypair: KeyPair) { + const sign = sodium.crypto_sign_detached( + hash, + Buffer.from(keypair.privateKey) + ); + + return sign; +} diff --git a/src/helpers/transactions/id.ts b/src/helpers/transactions/id.ts new file mode 100644 index 0000000..aee1c2b --- /dev/null +++ b/src/helpers/transactions/id.ts @@ -0,0 +1,17 @@ +import {fromBuffer} from '../bignumber'; +import {getHash, type SignedTransaction} from './hash'; + +export const getTransactionId = (transaction: SignedTransaction) => { + if (!transaction.signature) { + throw new Error('Transaction Signature is required'); + } + + const hash = getHash(transaction, {skipSignature: false}); + + const temp = Buffer.alloc(8); + for (let i = 0; i < 8; i++) { + temp[i] = hash[7 - i]; + } + + return fromBuffer(temp).toString(); +}; diff --git a/src/helpers/transactions/index.ts b/src/helpers/transactions/index.ts new file mode 100644 index 0000000..9f49aaa --- /dev/null +++ b/src/helpers/transactions/index.ts @@ -0,0 +1,199 @@ +import {signTransaction} from './hash'; + +import {createAddressFromPublicKey} from '../keys'; +import {getEpochTime} from '../time'; + +import {MessageType, TransactionType} from '../constants'; +import type {KeyPair} from '../keys'; +import type {AdamantAddress} from '../../api'; + +export type AnyTransactionData = ( + | SendTransactionData + | ChatTransactionData + | VoteTransactionData + | DelegateTransactionData + | StateTransactionData +) & { + transactionType: TransactionType; +}; + +export interface BasicTransactionData { + keyPair: KeyPair; +} + +export interface SendTransactionData extends BasicTransactionData { + recipientId: string; + amount: number; +} + +export interface ChatTransactionData extends BasicTransactionData { + recipientId: AdamantAddress; + message_type: MessageType; + amount: number | undefined; + message: string; + own_message: string; +} + +export interface VoteTransactionData extends BasicTransactionData { + votes: string[]; +} + +export interface DelegateTransactionData extends BasicTransactionData { + username: string; +} + +export interface StateTransactionData extends BasicTransactionData { + key: string; + value: string; +} + +interface BasicTransaction { + type: T; + timestamp: number; + amount: number; + senderPublicKey: string; + senderId: AdamantAddress; + asset: {}; +} + +export const createBasicTransaction = ( + data: AnyTransactionData +): BasicTransaction => { + const transaction = { + type: data.transactionType as T, + timestamp: getEpochTime(), + amount: 0, + senderPublicKey: data.keyPair.publicKey.toString('hex'), + senderId: createAddressFromPublicKey(data.keyPair.publicKey), + asset: {}, + }; + + return transaction; +}; + +export const createSendTransaction = (data: SendTransactionData) => { + const details = { + ...data, + transactionType: TransactionType.SEND, + }; + + const transaction = { + ...createBasicTransaction(details), + recipientId: details.recipientId, + amount: details.amount, + asset: {}, + }; + + const signature = signTransaction(transaction, details.keyPair); + + return { + ...transaction, + signature, + }; +}; + +export const createStateTransaction = (data: StateTransactionData) => { + const details = { + ...data, + transactionType: TransactionType.STATE, + }; + + const transaction = { + ...createBasicTransaction(details), + recipientId: null, + asset: { + state: { + key: details.key, + value: details.value, + type: 0 as const, + }, + }, + }; + + const signature = signTransaction(transaction, details.keyPair); + + return { + ...transaction, + signature, + }; +}; + +export const createChatTransaction = (data: ChatTransactionData) => { + const details = { + ...data, + transactionType: TransactionType.CHAT_MESSAGE, + }; + + const transaction = { + ...createBasicTransaction(details), + recipientId: details.recipientId, + amount: details.amount || 0, + asset: { + chat: { + message: data.message, + own_message: data.own_message, + type: data.message_type, + }, + }, + }; + + const signature = signTransaction(transaction, details.keyPair); + + return { + ...transaction, + signature, + }; +}; + +export const createDelegateTransaction = (data: DelegateTransactionData) => { + const details = { + ...data, + transactionType: TransactionType.DELEGATE, + }; + + const transaction = { + ...createBasicTransaction(details), + recipientId: null, + asset: { + delegate: { + username: details.username, + publicKey: details.keyPair.publicKey.toString('hex'), + }, + }, + }; + + const signature = signTransaction(transaction, details.keyPair); + + return { + ...transaction, + signature, + }; +}; + +export const createVoteTransaction = (data: VoteTransactionData) => { + const details = { + ...data, + transactionType: TransactionType.VOTE, + }; + + const basicTransaction = + createBasicTransaction(details); + + const transaction = { + ...basicTransaction, + recipientId: basicTransaction.senderId, + asset: { + votes: details.votes, + }, + }; + + const signature = signTransaction(transaction, details.keyPair); + + return { + ...transaction, + signature, + }; +}; + +export * from './hash'; +export * from './id'; diff --git a/src/helpers/transactions/tests/id.test.ts b/src/helpers/transactions/tests/id.test.ts new file mode 100644 index 0000000..41e3513 --- /dev/null +++ b/src/helpers/transactions/tests/id.test.ts @@ -0,0 +1,52 @@ +import {getTransactionId} from '../id'; + +describe('getTransactionId', () => { + it('should return the same id as a node for message transaction', () => { + const id = getTransactionId({ + type: 8, + amount: 0, + timestamp: 194049840, + asset: { + chat: { + message: '7189aba904138dd1d53948ed1e5b1d18a11ba1910834', + own_message: '8b717d0a9142e697cafd342c8f79f042c47a9e712e8a61b6', + type: 1, + }, + }, + recipientId: 'U12605277787100066317', + senderId: 'U8084717991279447871', + senderPublicKey: + '09c93f2667728c62d2279bbb8df34c3856088290167f557c33594dc212da054a', + signature: + '304a4cb7e11651d576e2c4dffb4100bef5385981807f18c3267c863daf60bd277706e6790157beacf5100c77b6798c4725f2f4e070ca78496ff53a4c2e437f02', + }); + + // https://ahead.adamant.im/api/transactions/get?id=5505818610983968576&returnAsset=1 + expect(id).toBe('5505818610983968576'); + }); + + it('should return the same id as a node for transfer transaction', () => { + const id = getTransactionId({ + type: 8, + amount: 0, + senderId: 'U3716604363012166999', + senderPublicKey: + '1ed651ec1c686c23249dadb2cb656edd5f8e7d35076815d8a81c395c3eed1a85', + asset: { + chat: { + message: + 'beeac1b98d27cb2052edaf37e2843838b25fa8eb6cccaa076a2b66db179207ff76a2233822218143fddbcb5d034da27d1a7b088bab2012b16ac9574995dadeaf2783afcaa6b960cdcd680761895b16f004736aea55f1fb46417fd2816da35c00960c2d40e9e5e96ab52d97c5d97fe72d2fca2a6ef5225dd46ad380edfa27de6bd8b0f3f3a6c8166da3bff716db4e42699d116668403da7eb742f640ffc69a7122111e1e0db9bf3f65ae6c3380d3436d7a6', + own_message: '1111240165c825cf31164cc05fb6d40b58d72493d8798390', + type: 2, + }, + }, + recipientId: 'U8084717991279447871', + timestamp: 194374197, + signature: + 'bf9912ab59fe93780433c18e27d3634d8e78112e53379bcac7fc52a46cf90071c147d9dee06497441928afdb6e59501dce679788b3322e97be93178d9f7c7c0d', + }); + + // https://ahead.adamant.im/api/transactions/get?id=10707920525275969664&returnAsset=1 + expect(id).toBe('10707920525275969664'); + }); +}); diff --git a/src/helpers/url.ts b/src/helpers/url.ts new file mode 100644 index 0000000..9bb7c22 --- /dev/null +++ b/src/helpers/url.ts @@ -0,0 +1,36 @@ +import dns from 'dns/promises'; + +const RE_HTTP_URL = /^https?:\/\/(.*)$/; +const RE_IP = + /(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}/; + +export const parseUrl = async (url: string) => { + const isHttps = url.startsWith('https'); + + // base url without port + const [baseURL] = url.replace(RE_HTTP_URL, '$1').split(':'); + + const ip = RE_IP.test(baseURL) ? baseURL : await retrieveIP(baseURL); + + return { + baseURL, + ip, + isHttps, + }; +}; + +export const retrieveIP = async (url: string) => { + try { + const addresses = await dns.resolve4(url); + + if (addresses) { + const [address] = addresses; + + if (address !== '0.0.0.0') { + return address; + } + } + } catch (error) { + return; + } +}; diff --git a/src/helpers/validator.js b/src/helpers/validator.js deleted file mode 100644 index 179ed6c..0000000 --- a/src/helpers/validator.js +++ /dev/null @@ -1,169 +0,0 @@ -const constants = require('./constants'); -const bigNumber = require('bignumber.js'); - -module.exports = { - getRandomIntInclusive(minimum, maximum) { - const min = Math.ceil(minimum); - const max = Math.floor(maximum); - - // The maximum is inclusive and the minimum is inclusive - return Math.floor(Math.random() * (max - min + 1) + min); - }, - isNumeric(str) { - if (typeof str !== 'string') { - return false; - } - - return !isNaN(parseFloat(str)); - }, - tryParseJSON(jsonString) { - try { - const o = JSON.parse(jsonString); - - return typeof o === 'object' ? o : false; - } catch (e) { - return false; - } - }, - validatePassPhrase(passPhrase) { - return typeof passPhrase === 'string' && passPhrase.length > 30; - }, - validateEndpoint(endpoint) { - return typeof endpoint === 'string'; - }, - validateAdmAddress(address) { - return typeof(address) === 'string' && constants.RE_ADM_ADDRESS.test(address); - }, - validateAdmPublicKey(publicKey) { - return ( - typeof publicKey === 'string' && - publicKey.length === 64 && - constants.RE_HEX.test(publicKey) - ); - }, - validateAdmVoteForPublicKey(publicKey) { - return ( - typeof publicKey === 'string' && - constants.RE_ADM_VOTE_FOR_PUBLIC_KEY.test(publicKey) - ); - }, - validateAdmVoteForAddress(address) { - return ( - typeof address === 'string' && - constants.RE_ADM_VOTE_FOR_ADDRESS.test(address) - ); - }, - validateAdmVoteForDelegateName(delegateName) { - return ( - typeof delegateName === 'string' && - constants.RE_ADM_VOTE_FOR_DELEGATE_NAME.test(delegateName) - ); - }, - validateIntegerAmount(amount) { - return Number.isSafeInteger(amount); - }, - validateStringAmount(amount) { - return this.isNumeric(amount); - }, - validateMessageType(messageType) { - return [1, 2, 3].includes(messageType); - }, - validateMessage(message, messageType) { - if (typeof message !== 'string') { - return { - result: false, - error: `Message must be a string`, - }; - } - - if ([2, 3].includes(messageType)) { - const json = this.tryParseJSON(message); - - if (!json) { - return { - result: false, - error: `For rich and signal messages, 'message' must be a JSON string`, - }; - } - - if (json.type && json.type.toLowerCase().includes('_transaction')) { - if (json.type.toLowerCase() !== json.type) { - return { - result: false, - error: `Value '_transaction' must be in lower case`, - }; - } - } - - if (typeof json.amount !== 'string' || !this.validateStringAmount(json.amount)) { - return { - result: false, - error: `Field 'amount' must be a string, representing a number`, - }; - } - } - - return { - result: true, - }; - }, - validateDelegateName(name) { - if (typeof name !== 'string') { - return false; - } - - return constants.RE_ADM_DELEGATE_NAME.test(name); - }, - admToSats(amount) { - return bigNumber(String(amount)) - .multipliedBy(constants.SAT) - .integerValue() - .toNumber(); - }, - async badParameter(name, value, customMessage) { - return { - success: false, - errorMessage: `Wrong '${name}' parameter${value ? ': ' + value : ''}${customMessage ? '. Error: ' + customMessage : ''}`, - }; - }, - formatRequestResults(response, isRequestSuccess) { - let results = { - details: {}, - }; - - if (isRequestSuccess) { - results = { - success: response.data && response.data.success, - data: response.data, - details: { - status: response.status, - statusText: response.statusText, - response: response, - }, - }; - - if (!results.success && results.data) { - results.errorMessage = `Node's reply: ${results.data.error}`; - } - } else { - results = { - success: false, - data: undefined, - details: { - status: response.status, - statusText: response.response && response.response.statusText, - response: response.response && response.response.status, - error: response.toString(), - message: ( - response.response && - response.response.data && - response.response.data.toString().trim() - ), - errorMessage: `${results.details.error}${results.details.message ? '. Message: ' + results.details.message : ''}`, - }, - }; - } - - return results; - }, -}; diff --git a/src/helpers/validator.ts b/src/helpers/validator.ts new file mode 100644 index 0000000..97bf0b2 --- /dev/null +++ b/src/helpers/validator.ts @@ -0,0 +1,146 @@ +import BigNumber from 'bignumber.js'; +import {MessageType, MessageTypes, SAT} from './constants'; +import type {AdamantAddress} from '../api'; + +export const getRandomIntInclusive = (minimum: number, maximum: number) => { + const min = Math.ceil(minimum); + const max = Math.floor(maximum); + + // The maximum is inclusive and the minimum is inclusive + return Math.floor(Math.random() * (max - min + 1) + min); +}; + +export const isNumeric = (str: unknown): str is string => + typeof str === 'string' && !isNaN(parseFloat(str)); + +export const parseJsonSafe = (json: string) => { + try { + const result = JSON.parse(json); + + return { + result, + success: true, + }; + } catch (error) { + return { + error, + success: false, + }; + } +}; + +export const isPassphrase = (passphrase: unknown): passphrase is string => + typeof passphrase === 'string' && passphrase.length > 30; + +export const isEndpoint = (endpoint: unknown): endpoint is string => + typeof endpoint === 'string' && endpoint.startsWith('/'); + +const RE_ADM_ADDRESS = /^U([0-9]{6,})$/; + +export const isAdmAddress = (address: unknown): address is AdamantAddress => + typeof address === 'string' && RE_ADM_ADDRESS.test(address); + +const RE_HEX = /^[a-fA-F0-9]+$/; + +export const isAdmPublicKey = (publicKey: unknown): publicKey is string => + typeof publicKey === 'string' && + publicKey.length === 64 && + RE_HEX.test(publicKey); + +const RE_ADM_VOTE_FOR_PUBLIC_KEY = /^(\+|-)[a-fA-F0-9]{64}$/; + +export const isAdmVoteForPublicKey = ( + publicKey: unknown +): publicKey is string => + typeof publicKey === 'string' && RE_ADM_VOTE_FOR_PUBLIC_KEY.test(publicKey); + +const RE_ADM_VOTE_FOR_ADDRESS = /^(\+|-)U([0-9]{6,})$/; + +export const isAdmVoteForAddress = (address: unknown) => + typeof address === 'string' && RE_ADM_VOTE_FOR_ADDRESS.test(address); + +const RE_ADM_VOTE_FOR_DELEGATE_NAME = /^(\+|-)([a-z0-9!@$&_]{1,20})$/; + +export const isAdmVoteForDelegateName = ( + delegateName: unknown +): delegateName is string => + typeof delegateName === 'string' && + RE_ADM_VOTE_FOR_DELEGATE_NAME.test(delegateName); + +export const isIntegerAmount = (amount: number) => Number.isSafeInteger(amount); + +export const isStringAmount = (amount: string) => isNumeric(amount); + +export const isMessageType = ( + messageType: number +): messageType is MessageTypes => [1, 2, 3].includes(messageType); + +export const validateMessage = ( + message: string, + messageType: MessageType = MessageType.Chat +) => { + if (typeof message !== 'string') { + return { + success: false, + error: 'Message should be a string', + }; + } + + if ([MessageType.Rich, MessageType.Signal].includes(messageType)) { + const data = parseJsonSafe(message); + + const {success, result} = data; + if (!success || typeof result !== 'object') { + return { + success: false, + error: "For rich and signal message, 'message' should be a JSON string", + }; + } + + const typeInLowerCase = result.type?.toLowerCase(); + if ( + typeInLowerCase?.includes('_transaction') && + typeInLowerCase !== result.type + ) { + return { + success: false, + error: "Value '_transaction' must be in lower case", + }; + } + + if (typeof result.amount !== 'string' || !isStringAmount(result.amount)) { + return { + success: false, + error: "Field 'amount' must be a string, representing a number", + }; + } + } + + return {success: true}; +}; + +const RE_ADM_DELEGATE_NAME = /^[a-z0-9!@$&_]{1,20}$/; + +export const isDelegateName = (name: unknown): name is string => + typeof name === 'string' && RE_ADM_DELEGATE_NAME.test(name); + +export const admToSats = (amount: number) => + new BigNumber(String(amount)).multipliedBy(SAT).integerValue().toNumber(); + +export const badParameter = ( + name: string, + value?: unknown, + message?: string +) => ({ + success: false, + error: `Wrong '${name}' parameter${value ? `: ${value}` : ''}${ + message ? `. Error: ${message}` + message : '' + }`, +}); + +export type AdamantApiResult = + | (Omit & {success: true}) + | { + success: false; + errorMessage: string; + }; diff --git a/src/helpers/wsClient.js b/src/helpers/wsClient.js deleted file mode 100644 index 8b0244d..0000000 --- a/src/helpers/wsClient.js +++ /dev/null @@ -1,91 +0,0 @@ -const ioClient = require('socket.io-client'); -const logger = require('./logger'); -const validator = require('./validator'); - -module.exports = { - isSocketEnabled: false, // If we need socket connection - wsType: 'ws', // Socket connection type, 'ws' (default) or 'wss' - admAddress: '', // ADM address to subscribe to notifications - connection: null, // Socket connection - onNewMessage: null, // Method to process new messages or transactions - activeNodes: [], // List of nodes that are active. Not all of them synced and support socket. - activeSocketNodes: [], // List of nodes that are active, synced and support socket - useFastest: false, // If to connect to node with minimum ping. Not recommended. - // Constructor - initSocket(params) { - this.onNewMessage = params.onNewMessage; - this.isSocketEnabled = params.socket; - this.wsType = params.wsType; - this.admAddress = params.admAddress; - }, - // Runs after every healthCheck() to re-connect socket if needed - reviseConnection(nodes) { - if (!this.isSocketEnabled) { - return; - } - if (!this.connection || !this.connection.connected) { - this.activeNodes = nodes.slice(); - this.setNodes(); - this.setConnection(); - } - }, - // Make socket connection and subscribe to new transactions - setConnection() { - if (!this.activeSocketNodes.length) { - return logger.warn(`[Socket] No supported socket nodes at the moment.`); - } - - const node = this.socketAddress(); - logger.log(`[Socket] Supported nodes: ${this.activeSocketNodes.length}. Connecting to ${node}...`); - this.connection = ioClient(node, {reconnection: false, timeout: 5000}); - - this.connection.on('connect', () => { - this.connection.emit('address', this.admAddress); - logger.info('[Socket] Connected to ' + node + ' and subscribed to incoming transactions for ' + this.admAddress + '.'); - }); - - this.connection.on('disconnect', (reason) => { - logger.warn('[Socket] Disconnected. Reason: ' + reason); - }); - - this.connection.on('connect_error', (err) => { - logger.warn('[Socket] Connection error: ' + err); - }); - - this.connection.on('newTrans', (transaction) => { - if ((transaction.recipientId === this.admAddress) && (transaction.type === 0 || transaction.type === 8)) { - // console.info(`[Socket] New incoming socket transaction received: ${transaction.id}`); - this.onNewMessage(transaction); - } - }); - }, - // Save the list of nodes activeSocketNodes that are active, synced and support socket - setNodes() { - this.activeSocketNodes = this.activeNodes.filter((n) => n.socketSupport & !n.outOfSync); - // Remove nodes without IP if 'ws' connection type - if (this.wsType === 'ws') { - this.activeSocketNodes = this.activeSocketNodes.filter((n) => !n.ifHttps || n.ip); - } - }, - // Returns socket url for connection - socketAddress() { - const node = this.useFastest ? this.fastestNode() : this.randomNode(); - let socketUrl = this.wsType + '://'; - if (this.wsType === 'ws') { - let host = node.ip; - if (!host || host === undefined) { - host = node.url; - } - socketUrl = socketUrl + host + ':' + node.wsPort; - } else { - socketUrl = socketUrl + node.url; // no port if wss - } - return socketUrl; - }, - fastestNode() { - return this.activeSocketNodes[0]; // They are sorted by ping - }, - randomNode() { - return this.activeSocketNodes[validator.getRandomIntInclusive(0, this.activeSocketNodes.length - 1)]; - }, -}; diff --git a/src/helpers/wsClient.ts b/src/helpers/wsClient.ts new file mode 100644 index 0000000..00debc8 --- /dev/null +++ b/src/helpers/wsClient.ts @@ -0,0 +1,323 @@ +import {io, type Socket} from 'socket.io-client'; + +import type {ActiveNode} from './healthCheck'; +import {Logger} from './logger'; +import {getRandomIntInclusive} from './validator'; +import {TransactionType} from './constants'; +import type { + AnyTransaction, + ChatMessageTransaction, + KVSTransaction, + RegisterDelegateTransaction, + TokenTransferTransaction, + VoteForDelegateTransaction, +} from '../api/generated'; +import type {AdamantAddress} from '../api'; + +export type WsType = 'ws' | 'wss'; + +export interface WsOptions { + /** + * ADM address to subscribe to notifications + */ + admAddress: AdamantAddress; + + /** + * Websocket type: `'wss'` or `'ws'`. `'wss'` is recommended. + */ + wsType?: WsType; + + /** + * Must connect to node with minimum ping. Not recommended. Default is `false`. + */ + useFastest?: boolean; + + logger?: Logger; +} + +type ErrorHandler = (error: unknown) => void; + +type TransactionMap = { + [TransactionType.SEND]: TokenTransferTransaction; + [TransactionType.DELEGATE]: RegisterDelegateTransaction; + [TransactionType.VOTE]: VoteForDelegateTransaction; + [TransactionType.CHAT_MESSAGE]: ChatMessageTransaction; + [TransactionType.STATE]: KVSTransaction; +}; + +type EventType = keyof TransactionMap; + +type TransactionHandler = (transaction: T) => void; + +type SingleTransactionHandler = + | TransactionHandler + | TransactionHandler + | TransactionHandler + | TransactionHandler + | TransactionHandler; + +type AnyTransactionHandler = TransactionHandler; + +export class WebSocketClient { + /** + * Web socket client options. + */ + public options: WsOptions; + + /** + * Current socket connection + */ + private connection?: Socket; + + /** + * List of nodes that are active, synced and support socket. + */ + private nodes: ActiveNode[]; + + private logger: Logger; + + private errorHandler: ErrorHandler; + private eventHandlers: { + [T in EventType]: TransactionHandler[]; + } = { + [TransactionType.SEND]: [], + [TransactionType.DELEGATE]: [], + [TransactionType.VOTE]: [], + [TransactionType.CHAT_MESSAGE]: [], + [TransactionType.STATE]: [], + }; + + constructor(options: WsOptions) { + this.logger = options.logger || new Logger(); + this.options = { + wsType: 'ws', + ...options, + }; + + this.nodes = []; + + this.errorHandler = (error: unknown) => { + this.logger.error(`${error}`); + }; + } + + /** + * Filters nodes that support websocket and connects to one of them. + * + * @param nodes Sorted by ping array of active nodes + */ + reviseConnection(nodes: ActiveNode[]) { + if (this.connection?.connected) { + return; + } + + const {wsType} = this.options; + + this.nodes = nodes.filter( + node => + node.socketSupport && + !node.outOfSync && + // Remove nodes without IP if 'ws' connection type + (wsType !== 'ws' || !node.isHttps || node.ip) + ); + + this.setConnection(); + } + + /** + * Chooses node and sets up connection. + */ + setConnection() { + const {logger} = this; + + const supportedCount = this.nodes.length; + if (!supportedCount) { + logger.warn('[Socket] No supported socket nodes at the moment.'); + return; + } + + const node = this.chooseNode(); + logger.log( + `[Socket] Supported nodes: ${supportedCount}. Connecting to ${node}...` + ); + const connection = io(node, { + reconnection: false, + timeout: 5000, + }); + + connection.on('connect', () => { + const {admAddress} = this.options; + + connection.emit('address', admAddress); + logger.info( + `[Socket] Connected to ${node} and subscribed to incoming transactions for ${admAddress}` + ); + }); + + connection.on('disconnect', reason => + logger.warn(`[Socket] Disconnected. Reason: ${reason}`) + ); + + connection.on('connect_error', error => + logger.warn(`[Socket] Connection error: ${error}`) + ); + + connection.on('newTrans', (transaction: AnyTransaction) => { + if (transaction.recipientId !== this.options.admAddress) { + return; + } + + this.handle(transaction); + }); + + this.connection = connection; + } + + /** + * Sets an error handler for all event handlers. + * + * @example + * ```js + * socket.onMessage(() => throw new Error('catch me')) + * + * socket.catch((error) => { + * console.log(error) // Error: catch me + * }) + * ``` + */ + public catch(callback: ErrorHandler) { + this.errorHandler = callback; + return this; + } + + /** + * Removes the handler from all types. + */ + public off(handler: SingleTransactionHandler) { + for (const handlers of Object.values(this.eventHandlers)) { + const index = (handlers as SingleTransactionHandler[]).indexOf(handler); + if (index !== -1) { + handlers.splice(index, 1); + } + } + + return this; + } + + /** + * Adds an event listener handler for all transaction types. + */ + public on(handler: AnyTransactionHandler): this; + /** + * Adds an event listener handler for the specific transaction types. + */ + public on( + types: T | T[], + handler: TransactionHandler + ): this; + public on( + typesOrHandler: T | T[] | AnyTransactionHandler, + handler?: TransactionHandler + ) { + if (handler === undefined) { + if (typeof typesOrHandler === 'function') { + for (const trigger of Object.keys(this.eventHandlers)) { + this.eventHandlers[+trigger as EventType].push(typesOrHandler); + } + } + } else { + const triggers = Array.isArray(typesOrHandler) + ? typesOrHandler + : [typesOrHandler]; + + for (const trigger of triggers) { + this.eventHandlers[trigger as T].push(handler); + } + } + + return this; + } + + /** + * Registers an event handler for Chatn Message transactions. + */ + public onMessage(handler: TransactionHandler) { + return this.on(TransactionType.CHAT_MESSAGE, handler); + } + + /** + * Registers an event handler for Token Transfer transactions. + */ + public onTransfer(handler: TransactionHandler) { + return this.on(TransactionType.SEND, handler); + } + + /** + * Registers an event handler for Register Delegate transactions. + */ + public onNewDelegate( + handler: TransactionHandler + ) { + return this.on(TransactionType.DELEGATE, handler); + } + + /** + * Registers an event handler for Vote for Delegate transactions. + */ + public onVoteForDelegate( + handler: TransactionHandler + ) { + return this.on(TransactionType.VOTE, handler); + } + + /** + * Registers an event handler for Key-Value Store (KVS) transactions. + */ + public onKVS(handler: TransactionHandler) { + return this.on(TransactionType.STATE, handler); + } + + private async handle(transaction: AnyTransaction) { + const handlers = this.eventHandlers[transaction.type as T]; + + for (const handler of handlers) { + try { + await handler(transaction as TransactionMap[T]); + } catch (error) { + this.errorHandler(error); + } + } + } + + /** + * Chooses fastest or random node based on {@link WsOptions.useFastest} option + * + * @returns WebSocket url + */ + chooseNode(): string { + const {wsType, useFastest} = this.options; + + const node = useFastest ? this.fastestNode() : this.randomNode(); + + let baseURL: string; + + if (wsType === 'ws') { + const host = node.ip ? node.ip : node.baseURL; + + baseURL = `${host}:${node.wsPort}`; + } else { + baseURL = node.baseURL; // no port if wss + } + + return `${wsType}://${baseURL}`; + } + + fastestNode() { + return this.nodes[0]; // They are already sorted by ping + } + + randomNode() { + const randomIndex = getRandomIntInclusive(0, this.nodes.length - 1); + return this.nodes[randomIndex]; + } +} diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 1b6d7bb..0000000 --- a/src/index.js +++ /dev/null @@ -1,65 +0,0 @@ -const get = require('./groups/get'); -const getPublicKey = require('./groups/getPublicKey'); -const decodeMsg = require('./groups/decodeMsg'); - -const newDelegate = require('./groups/newDelegate'); -const voteForDelegate = require('./groups/voteForDelegate'); -const sendTokens = require('./groups/sendTokens'); -const sendMessage = require('./groups/sendMessage'); -const healthCheck = require('./helpers/healthCheck'); - -const eth = require('./groups/eth'); -const dash = require('./groups/dash'); -const btc = require('./groups/btc'); -const doge = require('./groups/doge'); -const lsk = require('./groups/lsk'); - -const encryptor = require('./helpers/encryptor'); -const socket = require('./helpers/wsClient'); -const logger = require('./helpers/logger'); - -const constants = require('./helpers/constants.js'); -const transactionFormer = require('./helpers/transactionFormer'); -const keys = require('./helpers/keys'); - -/** - * Create new API instance - * @param {{ node: string, checkHealthAtStartup: boolean, logLevel: string, checkHealthTimeout: number}} params - * @param {{ error: function, warn: function, info: function, log: function }?} customLogger - * @param {function?} checkHealthAtStartupCallback callback which is called after the first health check or - * just at startup when params.checkHealthAtStartup is passed - * @return {object} API methods and helpers - */ -module.exports = (params, customLogger, checkHealthAtStartupCallback) => { - const log = customLogger || console; - - logger.initLogger(params.logLevel, log); - - const nodeManager = healthCheck( - params.node, - params.checkHealthAtStartup, - params.checkNodeTimeout, - checkHealthAtStartupCallback, - ); - - return { - get: get(nodeManager), - getPublicKey: getPublicKey(nodeManager), - sendTokens: sendTokens(nodeManager), - sendMessage: sendMessage(nodeManager), - newDelegate: newDelegate(nodeManager), - voteForDelegate: voteForDelegate(nodeManager), - setStartupCallback: nodeManager.setStartupCallback, - decodeMsg, - eth, - dash, - btc, - doge, - lsk, - transactionFormer, - keys, - encryptor, - socket, - constants, - }; -}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..5e89742 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,8 @@ +export * from './api/index'; +export * from './coins/index'; +export * from './helpers/transactions/index'; +export * from './helpers/encryptor'; +export * from './helpers/constants'; +export * from './helpers/logger'; +export * from './helpers/wsClient'; +export * from './helpers/keys'; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ff0e728 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "strict": true, + "lib": [], + "target": "ESNext", + "module": "CommonJS", + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "esModuleInterop": true + }, + "include": ["src/index.ts", "types/*.d.ts"], + "exclude": ["node_modules"] +} diff --git a/types/coininfo.d.ts b/types/coininfo.d.ts new file mode 100644 index 0000000..79401a2 --- /dev/null +++ b/types/coininfo.d.ts @@ -0,0 +1,30 @@ +declare module 'coininfo' { + export interface Network { + messagePrefix: string; + bech32: string; + bip32: Bip32; + pubKeyHash: number; + scriptHash: number; + wif: number; + } + + export interface Bip32 { + public: number; + private: number; + } + + export interface CoinInfo { + main: { + toBitcoinJS: () => Network; + }; + } + + export interface Coins { + bitcoin: CoinInfo; + dash: CoinInfo; + dogecoin: CoinInfo; + } + + const coininfo: Coins; + export default coininfo; +} diff --git a/types/socket-io-client.d.ts b/types/socket-io-client.d.ts new file mode 100644 index 0000000..957d8fd --- /dev/null +++ b/types/socket-io-client.d.ts @@ -0,0 +1,13 @@ +/** + * This is temporary workaround. + * + * `@types/node` doesn't expose `TransformStream` in global scope as it should + * which breaks `engine.io-parser` package that is dependency of `socket.io-client` + * + * @see https://github.com/socketio/engine.io-parser/issues/136 + */ +import {TransformStream as TS} from 'node:stream/web'; + +declare global { + type TransformStream = TS; +} diff --git a/types/sodium-browserify-tweetnacl.d.ts b/types/sodium-browserify-tweetnacl.d.ts new file mode 100644 index 0000000..f22ade1 --- /dev/null +++ b/types/sodium-browserify-tweetnacl.d.ts @@ -0,0 +1,17 @@ +declare module 'sodium-browserify-tweetnacl' { + namespace sodium { + export function crypto_sign_seed_keypair(seed: Buffer): { + publicKey: Buffer; + secretKey: Buffer; + }; + + export function crypto_sign_detached( + hash: Buffer, + privateKey: Buffer + ): Buffer; + + export function randombytes(nonce: Buffer): void; + } + + export = sodium; +}