diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d42f42d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +docs/ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b8fe33d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Set JavaScript file rules +[*.js] +charset = utf-8 +indent_style = space +indent_size = 2 + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..fcd70fb --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,70 @@ +module.exports = { + env: { + es2021: true, + 'shared-node-browser': true, + }, + extends: [ + 'airbnb-base', + 'prettier', + 'plugin:import/typescript', + 'eslint:recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 12, + }, + plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc'], + rules: { + 'no-restricted-globals': [ + 'error', + { + // This is to ensure that we use the 'buffer' package in the browser. In Node it doesn't + // make a difference. + name: 'Buffer', + message: + "Explictly import Buffer with `import { Buffer } from 'buffer'`", + }, + ], + 'no-constant-condition': ['error', { checkLoops: false }], + 'no-restricted-syntax': ['error', 'LabeledStatement', 'WithStatement'], + 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }], + 'max-classes-per-file': 'off', + 'import/extensions': [ + 'error', + 'ignorePackages', + { + js: 'never', + ts: 'never', + }, + ], + 'import/prefer-default-export': 'off', + 'no-continue': 'off', + 'lines-between-class-members': [ + 'error', + 'always', + { exceptAfterSingleLine: true }, + ], + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': ['error'], + 'no-redeclare': 'off', + '@typescript-eslint/no-redeclare': ['error'], + 'no-shadow': 'off', + '@typescript-eslint/no-shadow': ['error'], + }, + overrides: [ + { + files: ['**/*.ts'], + rules: { + 'import/no-commonjs': 'error', + 'tsdoc/syntax': 'warn', + }, + }, + ], + ignorePatterns: [ + 'dist/', + 'docs/', + 'tests/cucumber/features/', + 'tests/cucumber/browser/build/', + 'tests/browser/bundle.*', + ], +}; diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..1ea816c --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,20 @@ +changelog: + exclude: + labels: + - Skip-Release-Notes + categories: + - title: Bugfixes + labels: + - Bug-Fix + - title: New Features + labels: + - New Feature + - title: Enhancements + labels: + - Enhancement + - title: Not Yet Enabled + labels: + - Not-Yet-Enabled + - title: Other + labels: + - "*" diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml index ad8e490..749d620 100644 --- a/.github/workflows/create-release-pr.yml +++ b/.github/workflows/create-release-pr.yml @@ -31,14 +31,14 @@ jobs: echo "release-tag: $RELEASE_VERSION-$PRE_RELEASE_VERSION" echo "release-tag=$RELEASE_VERSION-$PRE_RELEASE_VERSION" >> $GITHUB_OUTPUT elif [[ -n $PRE_RELEASE_VERSION ]]; then - echo "Input pre_release_version is not empty, but does not match the regex pattern ^[a-z.0-9]+$" + echo "Input pre_release_version is not empty, but does not match the regex pattern ^[-a-z0-9]+$" exit 1 else echo "release-tag: $RELEASE_VERSION" echo "release-tag=$RELEASE_VERSION" >> $GITHUB_OUTPUT fi else - echo "Version input doesn't match the regex pattern ^v[0-9]+\.[0-9]+\.[0-9]+$" + echo "Version input doesn't match the regex pattern ^[0-9]+\.[0-9]+\.[0-9]+$" exit 1 fi @@ -78,52 +78,25 @@ jobs: fi - name: Build Changelog - uses: mikepenz/release-changelog-builder-action@v3.7.2 id: build-changelog env: PREVIOUS_VERSION: ${{ steps.get-release.outputs.latest-tag }} - with: - fromTag: ${{ env.PREVIOUS_VERSION }} - toTag: ${{ env.RELEASE_BRANCH }} - failOnError: true - configurationJson: | - { - "categories": [ - { - "title": "## New Features", - "labels": [ - "New Feature" - ] - }, - { - "title": "## Enhancement", - "labels": [ - "Enhancement" - ] - }, - { - "title": "## Bug Fixes", - "labels": [ - "Bug-Fix" - ] - }, - { - "title": "## Not Yet Enabled", - "labels": [ - "Not-Yet-Enabled" - ] - } - ], - "ignore_labels": [ - "Skip-Release-Notes" - ], - "sort": { - "order": "ASC", - "on_property": "mergedAt" - }, - "template": "#{{CHANGELOG}}", - "pr_template": "- #{{TITLE}} by #{{AUTHOR}} in ##{{NUMBER}}" - } + run: | + CHANGELOG=$(curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ github.token }}"\ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/${{ github.repository }}/releases/generate-notes \ + -d '{"tag_name":"${{ env.RELEASE_VERSION }}","target_commitish":"${{ env.RELEASE_BRANCH }}","previous_tag_name":"${{ env.PREVIOUS_VERSION }}","configuration_file_path":".github/release.yml"}' \ + | jq -r '.body') + + # The EOF steps are used to save multiline string in github: + # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#example-of-a-multiline-string + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + echo "changelog<<$EOF" >> $GITHUB_OUTPUT + echo -e "${CHANGELOG}" >> $GITHUB_OUTPUT + echo "$EOF" >> $GITHUB_OUTPUT - name: Update Changelog if: ${{ env.PRE_RELEASE_VERSION == '' }} @@ -131,8 +104,23 @@ jobs: CHANGELOG_CONTENT: ${{ steps.build-changelog.outputs.changelog }} PREVIOUS_VERSION: ${{ steps.get-release.outputs.latest-tag }} run: | - echo "$(tail -n +2 CHANGELOG.md)" > CHANGELOG.md - echo -e "# Changelog\n\n# ${RELEASE_VERSION}\n\n${CHANGELOG_CONTENT}**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_VERSION}...${RELEASE_VERSION}" | cat - CHANGELOG.md > temp && mv temp CHANGELOG.md + echo -e "# ${RELEASE_VERSION}\n\n${CHANGELOG_CONTENT}" | cat - CHANGELOG.md > temp && mv temp CHANGELOG.md + + - name: Use Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Update Version References in Source + env: + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} + run: | + rm -rf dist node_modules + npm ci + npm run build + NEW_HASH=$(cat dist/browser/algosdk.min.js | openssl dgst -sha384 -binary | openssl base64 -A) + python3 scripts/bump_version.py ${RELEASE_TAG:1} --new_hash "${NEW_HASH}" + npx prettier --write . - name: Commit Changes uses: EndBug/add-and-commit@v9.1.3 @@ -148,16 +136,21 @@ jobs: GH_TOKEN: ${{ github.token }} RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} run: | - echo -e "# ${RELEASE_TAG}\n\n${CHANGELOG_CONTENT}**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_VERSION}...${RELEASE_TAG}" > msg_body.txt - export msg_body=$(cat msg_body.txt) + # Note: There's an issue adding teams as reviewers, see https://github.com/cli/cli/issues/6395 PULL_REQUEST_URL=$(gh pr create --base "master" \ --title "FOR REVIEW ONLY: ${{ github.event.repository.name }} $RELEASE_TAG" \ --label "Skip-Release-Notes" \ - --body "$msg_body" | tail -n 1) - PULL_REQUEST_NUM=$(echo $PULL_REQUEST_URL | sed 's:.*/::') - echo "pull-request-master=$PULL_REQUEST_URL" >> $GITHUB_ENV - echo "pull-request-master-num=$PULL_REQUEST_NUM" >> $GITHUB_ENV - echo "Pull request to Master created: $PULL_REQUEST_URL" + --label "Team Hyper Flow" \ + --body "${CHANGELOG_CONTENT}" | tail -n 1) + if [[ $PULL_REQUEST_URL =~ ^https://github.com/${{ github.repository }}/pull/[0-9]+$ ]]; then + PULL_REQUEST_NUM=$(echo $PULL_REQUEST_URL | sed 's:.*/::') + echo "pull-request-master=$PULL_REQUEST_URL" >> $GITHUB_ENV + echo "pull-request-master-num=$PULL_REQUEST_NUM" >> $GITHUB_ENV + echo "Pull request to Master created: $PULL_REQUEST_URL" + else + echo "There was an issue creating the pull request to master branch." + exit 1 + fi - name: Create Pull Request to Develop if: ${{ env.PRE_RELEASE_VERSION == '' }} @@ -165,13 +158,20 @@ jobs: GH_TOKEN: ${{ github.token }} RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} run: | + # Note: There's an issue adding teams as reviewers, see https://github.com/cli/cli/issues/6395 PULL_REQUEST_URL=$(gh pr create --base "develop" \ --title "FOR REVIEW ONLY: Merge back ${{ github.event.repository.name }} $RELEASE_TAG to develop" \ --label "Skip-Release-Notes" \ + --label "Team Hyper Flow" \ --body "Merge back version changes to develop." | tail -n 1) - echo "Pull request to Develop created: $PULL_REQUEST_URL" - DEVELOP_PR_MESSAGE="\nPull Request to develop: $PULL_REQUEST_URL" - echo "pull-request-develop-message=$DEVELOP_PR_MESSAGE" >> $GITHUB_ENV + if [[ $PULL_REQUEST_URL =~ ^https://github.com/${{ github.repository }}/pull/[0-9]+$ ]]; then + echo "Pull request to Develop created: $PULL_REQUEST_URL" + DEVELOP_PR_MESSAGE="\nPull Request to develop: $PULL_REQUEST_URL" + echo "pull-request-develop-message=$DEVELOP_PR_MESSAGE" >> $GITHUB_ENV + else + echo "There was an issue creating the pull request to develop branch." + exit 1 + fi - name: Send Slack Message id: slack diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6bf3410 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +.DS_Store + +.idea/ +# Webstorm +*.iml +.vscode/* +!.vscode/settings.json +!.vscode/extensions.json + +# npm +node_modules/ + +# Testing files +*.feature +temp + +# Environment information +examples/.env + +test-harness/ +tests/cucumber/features/ +tests/cucumber/browser/build +tests/browser/bundle.* + +# Builds +dist/ +docs/ +built/ + +# Caches +.eslintcache diff --git a/.huskyrc.js b/.huskyrc.js new file mode 100644 index 0000000..d614502 --- /dev/null +++ b/.huskyrc.js @@ -0,0 +1,5 @@ +module.exports = { + hooks: { + 'pre-commit': 'lint-staged', + }, +}; diff --git a/.lintstagedrc.js b/.lintstagedrc.js new file mode 100644 index 0000000..463efc4 --- /dev/null +++ b/.lintstagedrc.js @@ -0,0 +1,4 @@ +module.exports = { + '*.{js,ts,md,json,yml}': 'prettier --write', + '*.{js,ts}': 'eslint --cache --fix', +}; diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..54f8650 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +dist +docs +tests/cucumber/features +tests/cucumber/browser/build +tests/browser/bundle.* +.github +CODE_OF_CONDUCT.md diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..544138b --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/.test-env b/.test-env new file mode 100644 index 0000000..049289d --- /dev/null +++ b/.test-env @@ -0,0 +1,16 @@ +# Configs for testing repo download: +SDK_TESTING_URL="https://github.com/algorand/algorand-sdk-testing" +SDK_TESTING_BRANCH="master" +SDK_TESTING_HARNESS="test-harness" + +INSTALL_ONLY=0 + +VERBOSE_HARNESS=1 + +# WARNING: If set to 1, new features will be LOST when downloading the test harness. +# REGARDLESS: modified features are ALWAYS overwritten. +REMOVE_LOCAL_FEATURES=0 + +# WARNING: Be careful when turning on the next variable. +# In that case you'll need to provide all variables expected by `algorand-sdk-testing`'s `.env` +OVERWRITE_TESTING_ENVIRONMENT=0 diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..d7df89c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0ba8094 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.formatOnPaste": true +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e58f50..f701b54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,30 +1,771 @@ -# Changelog +# v7.7.5 -# v1.1.1 + -## Enhancement +## What's Changed + +### Other + +- Update CHANGELOG.md by @algobarb in https://github.com/algobarb/test/pull/38 +- Update README.md by @algobarb in https://github.com/algobarb/test/pull/98 + +**Full Changelog**: https://github.com/algobarb/test/compare/1.0.0...v7.7.5 + +# v2.5.0 + +## What's Changed + +### Bugfixes + +- docs: fix error message by @barnjamin in https://github.com/algorand/js-algorand-sdk/pull/780 + +### New Features + +- Algod: Simulation run with extra budget per transaction group by @ahangsu in https://github.com/algorand/js-algorand-sdk/pull/784 + +### Enhancements + +- DevOps: Update CODEOWNERS to only refer to the devops group by @onetechnical in https://github.com/algorand/js-algorand-sdk/pull/783 +- Client: Don't send auth header when token is empty by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/782 +- algod: Add state delta APIs by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/785 +- deps: Remove coveralls, mocha lcov by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/787 +- algod: Add json query arg to delta endpoints by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/793 + +### Other + +- Bump semver-regex from 3.1.2 to 3.1.4 by @dependabot in https://github.com/algorand/js-algorand-sdk/pull/580 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v2.3.0...v2.4.0 + +# v2.3.0 + +## What's Changed -- Update CHANGELOG.md by algobarb in #38 +### Bugfixes + +- Docs: Repair the links in the docs to developer.algorand.org by @bbroder-algo in https://github.com/algorand/js-algorand-sdk/pull/755 +- Fix: Allow nonParticipation flag to be a boolean by @barnjamin in https://github.com/algorand/js-algorand-sdk/pull/757 +- BugFix: Min fee sp fix by @barnjamin in https://github.com/algorand/js-algorand-sdk/pull/760 + +### New Features + +- Simulation: Lift log limits option in SimulateRequest by @ahangsu in https://github.com/algorand/js-algorand-sdk/pull/768 + +### Enhancements -**Full Changelog**: https://github.com/algobarb/test/compare/1.0.0...v1.1.1 +- Docs: Examples by @barnjamin in https://github.com/algorand/js-algorand-sdk/pull/754 +- API: Support updated simulate endpoint by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/764 +- algod: Add blockOffsetTimestamp, syncRound APIs to algod client by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/769 +- node: Drop support for node v14, npm audit fix by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/773 +- client: Export token header types by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/772 +- DevOps: Add CODEOWNERS to restrict workflow editing by @onetechnical in https://github.com/algorand/js-algorand-sdk/pull/775 +- Docs: Update README & FAQ by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/774 + +## New Contributors + +- @bbroder-algo made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/755 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v2.2.0...v2.3.0 # v2.2.0 ## What's Changed -fd +### Bugfixes + +- bugfix: Satisfy typescript constraints by @aorumbayev in https://github.com/algorand/js-algorand-sdk/pull/741 +- algod: Minor improvements to simulation support by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/749 + +### Enhancements + +- CICD: Update ssh fingerprint for gh-pages by @algobarb in https://github.com/algorand/js-algorand-sdk/pull/745 +- Docs: add examples to be pulled in for docs by @joe-p in https://github.com/algorand/js-algorand-sdk/pull/747 +- algod: Simulate Endpoint by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/743 + +## New Contributors + +- @aorumbayev made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/741 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v2.1.0...v2.2.0 + +# v2.1.0 + +## What's Changed + +### Enhancements + +- Enhancement: Add foreign array objects to ATC `addMethodCall` by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/725 +- API: verifyTealSign utility function by @M-Picco in https://github.com/algorand/js-algorand-sdk/pull/735 +- Packaging: Don't use global `Buffer` object by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/733 +- algod REST API: Add support for algod /v2/teal/disassemble by @michaeldiamant in https://github.com/algorand/js-algorand-sdk/pull/702 + +## New Contributors + +- @M-Picco made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/735 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v2.0.0...v2.1.0 + +# v2.0.0 + +## What's Changed + +### Breaking changes + +- Remove v1 algod API (`client.algod`) due to API end-of-life (2022-12-01). Instead, use v2 algod API (`client.v2.algod.algod`). +- Remove `cost` field in `DryrunTxnResult` in favor of 2 fields: `budget-added` and `budget-consumed`. `cost` can be derived by `budget-consumed - budget-added`. +- Remove logicsig templates (`logicTemplates`), `logic/langspec.json`, `logic.logic` depending on `langspec.json`. +- Regenerate algod models so every constructor requires an object to be passed in. Previously, only constructors with more than 4 argument specified this. +- Remove unused generated types: `CatchpointAbortResponse`, `CatchpointStartResponse`. +- Remove following methods in favor of the methods with `WithSuggestedParams` suffix: + - `makePaymentTxn`, `makeKeyRegistrationTxn`, `makeAssetCreateTxn`, `makeAssetConfigTxn`, `makeAssetDestroyTxn`, `makeAssetFreezeTxn`, `makeAssetTransferTxn`. +- Remove `makeLogicSig` in favor of either using `LogicSigAccount` (preferred) or directly invoking `LogicSig` constructor. +- Remove `EncodedMultisigBlob` in favor of `EncodedSignedTransaction. + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v1.24.1...v2.0.0 + +# v1.24.1 + +## What's Changed + +### Enhancements + +- Packaging: Improve source map and browser usage for external bundlers by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/707 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v1.24.0...v1.24.1 + +# v1.24.0 + +## What's Changed + +### Bugfixes + +- Bug-Fix: encode ABI string with non-ASCII characters by @ahangsu in https://github.com/algorand/js-algorand-sdk/pull/700 + +### Enhancements + +- Tests: Migrate v1 algod dependencies to v2 in cucumber tests by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/693 +- REST API: Add KV counts to NodeStatusResponse by @michaeldiamant in https://github.com/algorand/js-algorand-sdk/pull/696 +- Fix: createMultisigTransaction name in comments by @nullun in https://github.com/algorand/js-algorand-sdk/pull/694 +- Enhancement: allowing zero-length static array by @ahangsu in https://github.com/algorand/js-algorand-sdk/pull/698 +- ABI: Refactor ABI encoding test to round-trip by @michaeldiamant in https://github.com/algorand/js-algorand-sdk/pull/701 + +## New Contributors + +- @nullun made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/694 -**Full Changelog**: https://github.com/algorand/py-algorand-sdk/compare/v2.1.2...v2.2.0 +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v1.23.2...v1.24.0 -# v2.1.2 +# v1.23.2 ## What's Changed -This release adds a fix to allow disambiguation of transaction finality in the case of a decoding error. +### Bugfixes + +- SDK: Dryrun and transaction decoding fix for boxes by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/690 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v1.23.1...v1.23.2 + +# v1.23.1 ### Bugfixes -* ATC: Refactor Pending Transaction Information in ATC into try block by @algochoi in https://github.com/algorand/py-algorand-sdk/pull/451 -**Full Changelog**: https://github.com/algorand/py-algorand-sdk/compare/v2.1.1...v2.1.2 +- fix: mergeMultisigTransactions logic error by @AlgoDoggo in https://github.com/algorand/js-algorand-sdk/pull/675 +- CI: Remove unneeded dependency & update package-lock by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/684 + +### New Features + +- Boxes: Add support for Boxes by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/604 + +### Enhancements + +- Enhancement: Code generation improvements by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/663 +- Network: Replace `superagent` with `fetch` for HTTP requests by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/676 +- API: Fix docs typo by @PabloLION in https://github.com/algorand/js-algorand-sdk/pull/677 +- CI: Upgrade node.js from v12 to v14 by @michaeldiamant in https://github.com/algorand/js-algorand-sdk/pull/680 + +## New Contributors + +- @PabloLION made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/677 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v1.22.0...v1.23.1 + +# v1.22.0 + +## What's Changed + +### Bugfixes + +- Bug-Fix: Fix typo in documentation for `searchAccounts` `currencyGreaterThan` by @fionnachan in https://github.com/algorand/js-algorand-sdk/pull/572 + +### Enhancements + +- REST API: Add algod block hash endpoint, add indexer block header-only param. by @winder in https://github.com/algorand/js-algorand-sdk/pull/665 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v1.21.0...v1.22.0 + +# v1.21.0 + +## What's Changed + +### Enhancements + +- Enhancement: Removing more unused steps by @tzaffi in https://github.com/algorand/js-algorand-sdk/pull/637 +- Enhancement: Add deprecation tag to algod v1 client by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/642 +- enhancement: add unit test for ParticipationUpdates field by @shiqizng in https://github.com/algorand/js-algorand-sdk/pull/652 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v1.20.0...v1.21.0 + +# v1.20.0 + +## What's Changed + +### Bugfixes + +- Bug-Fix: Pass verbosity to the harness and sandbox by @tzaffi in https://github.com/algorand/js-algorand-sdk/pull/630 + +### Enhancements + +- Enhancement: Use sandbox for SDK Testing and remove Indexer v1 steps by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/623 +- Tidy: Ignore algorand-sdk-testing test-harness dir by @michaeldiamant in https://github.com/algorand/js-algorand-sdk/pull/634 +- Enhancement: Deprecating use of langspec by @ahangsu in https://github.com/algorand/js-algorand-sdk/pull/632 +- enhancement: Initial stateproofs support by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/629 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v1.19.1...v1.20.0 + +# v1.20.0-beta.1 + +## What's Changed + +### Bugfixes + +- Bug-Fix: Pass verbosity to the harness and sandbox by @tzaffi in https://github.com/algorand/js-algorand-sdk/pull/630 + +### Enhancements + +- Enhancement: Use sandbox for SDK Testing and remove Indexer v1 steps by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/623 +- Tidy: Ignore algorand-sdk-testing test-harness dir by @michaeldiamant in https://github.com/algorand/js-algorand-sdk/pull/634 +- Enhancement: Deprecating use of langspec by @ahangsu in https://github.com/algorand/js-algorand-sdk/pull/632 +- enhancement: Initial stateproofs support by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/629 + +## New Contributors + +- @ahangsu made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/632 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v1.19.1...v1.20.0-beta.1 + +# v1.19.1 + +### Enhancements + +- API: Support attaching signatures to standard and multisig transactions by @jdtzmn in https://github.com/algorand/js-algorand-sdk/pull/595 +- AVM: Consolidate TEAL and AVM versions by @michaeldiamant in https://github.com/algorand/js-algorand-sdk/pull/609 +- Testing: Use Dev mode network for cucumber tests by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/614 + +# v1.19.0 + +## What's Changed + +### Bugfixes + +- tech-debt: Remove unused/unmaintained templates by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/607 + +### New Features + +- Dev Tools: Source map decoder by @barnjamin in https://github.com/algorand/js-algorand-sdk/pull/590 +- Enhancement: Upgrade typedoc and plugins by @fionnachan in https://github.com/algorand/js-algorand-sdk/pull/605 + +### Enhancements + +- Github-Actions: Adding PR title and label checks by @algojack in https://github.com/algorand/js-algorand-sdk/pull/600 +- docs: tealSign by @AlgoDoggo in https://github.com/algorand/js-algorand-sdk/pull/610 + +# v1.18.1 + +## What's Changed + +- Properly set maxWidth in trace by @joe-p in https://github.com/algorand/js-algorand-sdk/pull/593 +- fix: safe intDecoding by @AlgoDoggo in https://github.com/algorand/js-algorand-sdk/pull/599 +- Remove code that relies on node's path module by @bmdelacruz in https://github.com/algorand/js-algorand-sdk/pull/598 + +## New Contributors + +- @AlgoDoggo made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/599 +- @bmdelacruz made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/598 + +# v1.18.0 + +## What's Changed + +- Add method to abi results by @barnjamin in https://github.com/algorand/js-algorand-sdk/pull/578 +- Add getMethodByName function to Contract and Interface by @barnjamin in https://github.com/algorand/js-algorand-sdk/pull/583 + +# v1.17.0 + +## What's Changed + +- Allow Uint8Arrays public keys for kmd signing by @vividn in https://github.com/algorand/js-algorand-sdk/pull/549 +- Update generated files by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/569 +- Build: Add SDK code generation workflow by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/570 +- Update codegen.yml by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/574 +- Generate updated client API code by @algoidurovic in https://github.com/algorand/js-algorand-sdk/pull/566 + +## New Contributors + +- @vividn made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/549 +- @Eric-Warehime made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/569 +- @algoidurovic made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/566 + +# v1.16.0 + +## Added + +- Dryrun stack printer +- Document more Indexer methods + +## Fixed + +- Corrected type of KMD keys +- Include foreign app addr in dryrun requests + +# v1.15.0 + +## Added + +- Support unlimited assets REST API changes. (#527) + +## Fixed + +- Fix app creation createDryrun error (#539) +- Fix cucumber tests for asset lookup step (#540) +- Fix searchForApplications filter by creator parameter type. (#546) + +# v1.15.0-beta.1 + +## Added + +- Support unlimited assets REST API changes. (#527) + +## Fixed + +- Fix app creation createDryrun error (#539) +- Fix cucumber tests for asset lookup step (#540) + +# v1.14.0 + +## Added + +- Add stateproof keyreg field (#463) + +## Changed + +- Implement C2C tests (#498) +- Moving from travis to circleci (#507) +- Add installation instructions for vite users (#512) +- Update langspec for TEAL 6 (#518) +- Docs for `lookupAssetByID` and `lookupAccountTransactions` (#516) +- Make FromObject parameter IntelliSense human readable (#528) +- Bump url-parse from 1.5.1 to 1.5.8 (#529) + +## Fixed + +- Use HTTP request format arg to determine response type (#532) +- Update chromedriver (#535) + +# v1.14.0-beta.1 + +## Added + +- Add stateproof keyreg field (#463) + +## Changed + +- Implement C2C tests (#498) +- Moving from travis to circleci (#507) +- Add installation instructions for vite users (#512) +- Update langspec for TEAL 6 (#518) + +# 1.13.1 + +## Added: + +- Add app creator to dryrun request (#499) + +## Changed: + +- Adding note to use bigint (#501) + +## Fixed: + +- Fix JSON decoding (#502) + +# 1.13.0 + +## Added: + +- Create dryrun (#478) +- Support ABI reference types and other improvements (#482) +- Improve HTTP error messages (#485) +- Enabling a custom client for Algod and Indexer (#477) +- Export sdk subclasses to typedoc (#479) +- ABI Support for JS library (#454) +- ABI interaction support (#466) +- Wait for confirmation function (#469) +- Add freezeAccount encoding in display method (#460) +- Regenerate code from specification file (#456) + +## Changed: + +- Document Indexer methods (#491) +- Document Algodv2 methods (#486) +- Update chromedriver (#492) + +## Fixed: + +- Fix wait for confirmation function (#480) +- Fix type for foreignAssets (#472) + +# 1.13.0-beta.2 + +## Added + +- Support ABI reference types and other improvements (#482) +- Improve HTTP error messages (#485) +- Enabling a custom client for Algod and Indexer (#477) +- Export sdk subclasses to typedoc (#479) + +## Fixed + +- Fix wait for confirmation function (#480) + +# 1.13.0-beta.1 + +## Added + +- ABI Support for JS library (#454) +- ABI interaction support (#466) +- Wait for confirmation function (#469) +- Add freezeAccount encoding in display method (#460) +- Regenerate code from specification file (#456) + +## Fixed + +- Fix type for foreignAssets (#472) + +# 1.12.0 + +## Added + +- Support AVM 1.0 +- Support deserializing nonparticipating and offline keyreg +- Support deserializing nonparticipating transaction + +## Fixed + +- Key registration transaction with nonParticipation=true + +# 1.11.1 + +## Fixed + +- Properly decode transaction extra pages field (#419) + +# 1.11.0 + +## Added + +- Signing support for rekeying to LogicSig/MultiSig account + +# 1.10.1 + +## Added + +- Add missing fields `msig` and `lsig` to `EncodedSignedTransaction` type +- Add the missing type `SignedTransaction`, which helped fix the `any` return value for `Transaction.from_obj_for_encoding` +- More internal types are now exported +- Support the new base64 asset fields in algod models +- Add ability to install the package from a git URL with npm + +## Fixed + +- Remove BigInt literals from package +- Support encoding transactions with a first round of zero +- Fix msgpack encoding of dryrun objects + +# 1.10.0 + +## Added + +- Support for dynamic opcode accounting, backward jumps, loops, callsub, retsub +- Ability to pool fees +- Ability to pay for extra pages + +## Changed + +- Add link to docs in readme +- Update examples on getting `suggestedParams` +- Grammatical fixes + +## Fixed + +- Fix asset creation transaction types that should be optional +- Remove synthetic default imports +- Use DryrunRequest instead of DryrunSource in constructor + +# 1.9.1 + +## Changed + +- Changed our browser bundle from webpack's `window` type to `UMD`, which fixes issues when using + the library from React ([#352](https://github.com/algorand/js-algorand-sdk/issues/352)). + +# 1.9.0 + +## Added + +- TypeScript support ([#302](https://github.com/algorand/js-algorand-sdk/pull/302), [#314](https://github.com/algorand/js-algorand-sdk/pull/314), [#315](https://github.com/algorand/js-algorand-sdk/pull/315), [#317](https://github.com/algorand/js-algorand-sdk/pull/317), [#313](https://github.com/algorand/js-algorand-sdk/pull/313), [#319](https://github.com/algorand/js-algorand-sdk/pull/319), [#323](https://github.com/algorand/js-algorand-sdk/pull/323), [#318](https://github.com/algorand/js-algorand-sdk/pull/318), [#331](https://github.com/algorand/js-algorand-sdk/pull/331), [#325](https://github.com/algorand/js-algorand-sdk/pull/325), [#337](https://github.com/algorand/js-algorand-sdk/pull/337)). +- Allow BigInts to be used to construct Transactions ([#263](https://github.com/algorand/js-algorand-sdk/pull/263)). +- `decodeAddress` now verifies the address checksum ([#269](https://github.com/algorand/js-algorand-sdk/pull/269)). +- Add support for nonparticipating key registration transactions ([#271](https://github.com/algorand/js-algorand-sdk/pull/271)). +- Allow LogicSigs to sign transactions with a different AuthAddr ([#268](https://github.com/algorand/js-algorand-sdk/pull/268)). +- Support for decoding BigInts from API calls ([#260](https://github.com/algorand/js-algorand-sdk/pull/260)). +- Add helper functions to encode and decode integers ([#281](https://github.com/algorand/js-algorand-sdk/pull/281)). +- Support new features from indexer v2.3.2 ([#296](https://github.com/algorand/js-algorand-sdk/pull/296)). +- Support TEAL 3 programs ([#294](https://github.com/algorand/js-algorand-sdk/pull/294)). + +## Fixed + +- Properly validate `assetMetadataHash` and `lease` ([#253](https://github.com/algorand/js-algorand-sdk/pull/253), [#280](https://github.com/algorand/js-algorand-sdk/pull/280)). +- Fix the `Algodv2.versionsCheck().do()` method ([#258](https://github.com/algorand/js-algorand-sdk/pull/258)). +- Fix an issue using `mergeMultisigTransactions` in React ([#259](https://github.com/algorand/js-algorand-sdk/pull/259)). +- Fix the inability to specify rekey addresses in several makeTransaction functions ([#267](https://github.com/algorand/js-algorand-sdk/pull/267)). +- Stop the Transaction constructor from modifying input arrays ([#279](https://github.com/algorand/js-algorand-sdk/pull/279)). +- Allow `signLogicSigTransaction` to accept Transaction objects ([#290](https://github.com/algorand/js-algorand-sdk/pull/290)). + +## Changed + +- Update examples to use v2 endpoints ([#289](https://github.com/algorand/js-algorand-sdk/pull/289)). +- Improve error trace reporting ([#291](https://github.com/algorand/js-algorand-sdk/pull/291)). +- Establish consistent code style ([#299](https://github.com/algorand/js-algorand-sdk/pull/299)). +- Remove `dist` folder from repo ([#326](https://github.com/algorand/js-algorand-sdk/pull/326)). + +# 1.8.1 + +## Added + +- Added `toString` and print methods to Transaction ([#243](https://github.com/algorand/js-algorand-sdk/pull/243)). +- Added functions to create Transactions from objects ([#246](https://github.com/algorand/js-algorand-sdk/pull/246)). + +## Fixed + +- Fixed issues using the library with webpack, including switching dependencies from `keccak` to `js-sha3` ([#247](https://github.com/algorand/js-algorand-sdk/pull/247)). + +# 1.8.0 + +## Added + +- Add `encodeAddress` and `decodeAddress` to convert between the binary and text form of Algorand + addresses ([#216](https://github.com/algorand/js-algorand-sdk/pull/216)). +- Add `encodeUnsignedTransaction`, `decodeUnsignedTransaction`, `decodeSignedTransaction` to convert + between binary transactions and transaction objects ([#218](https://github.com/algorand/js-algorand-sdk/pull/218)). +- Add optional `rekeyTo` parameter to transaction builder functions ([#221](https://github.com/algorand/js-algorand-sdk/pull/221)). +- Support testing on Chrome and Firefox in addition to Node ([#228](https://github.com/algorand/js-algorand-sdk/pull/228) and [#235](https://github.com/algorand/js-algorand-sdk/pull/235)). + +## Fixed + +- Update [keccak](https://www.npmjs.com/package/keccak) to 3.0.1, which fixes a build error that + would occur every time the package was installed ([#151](https://github.com/algorand/js-algorand-sdk/pull/151)). +- Allow `assignGroupID` to accept raw transaction objects and instances of the `Transaction` class + ([#236](https://github.com/algorand/js-algorand-sdk/pull/236)). +- Allow `signTransaction` to accept instances of the `Transaction` class ([#233](https://github.com/algorand/js-algorand-sdk/pull/233)). +- Improve type checking and documentation ([#233](https://github.com/algorand/js-algorand-sdk/pull/233) and [#231](https://github.com/algorand/js-algorand-sdk/pull/231)). + +## Changed + +- Switch to using [algo-msgpack-with-bigint](https://www.npmjs.com/package/algo-msgpack-with-bigint), + which is a fork of [@msgpack/msgpack](https://www.npmjs.com/package/@msgpack/msgpack) with support + for encoding and decoding BigInts ([#229](https://github.com/algorand/js-algorand-sdk/pull/229)). +- Update dependencies ([#237](https://github.com/algorand/js-algorand-sdk/pull/237)). + +# 1.7.2 + +## Fixed + +- Fixed msgpack endpoints returning undefined in browsers ([#210](https://github.com/algorand/js-algorand-sdk/pull/210) and [#215](https://github.com/algorand/js-algorand-sdk/pull/215)). +- Removed use of class properties ([#213](https://github.com/algorand/js-algorand-sdk/pull/213)). + +## Changed + +- Remove unneeded dependency js-yaml and changed mock-http-server to a dev dependency ([#214](https://github.com/algorand/js-algorand-sdk/pull/214) and [#212](https://github.com/algorand/js-algorand-sdk/pull/212)). + +# 1.7.1 + +## Fixed + +- Fixed set Accept on GET calls + +## Changed + +- Change algosdk.signMultisigTransaction to accept either a built Transaction or a dict of constructor args + +# 1.7.0 + +## Added + +- Support for Application Call Transactions, also known as Stateful TEAL +- Support for TEAL Compile and Dryrun +- Support for Rekeying Transactions + +## Fixed + +- An encoding failure due to an empty field will now indicate which field was empty +- Browserify can now handle newly exported modelsv2 package + +# 1.6.2 + +## Fixed + +- Fixed bug where submitting an array of transactions to v2 client's sendRawTransaction would cause an error. + +# 1.6.1 + +## Fixed + +- Fixed bug where Indexer and algod V2 clients were omitted from module exports. + +# 1.6.0 + +# Added + +- Clients for Indexer and algod V2 + +# 1.5.0 + +# Added + +- additional Algorand Smart Contracts (ASC) + - support for Dynamic Fee contract + - support for Limit Order contract + - support for Periodic Payment contract +- support for Suggested Params + +# 1.4.1 + +# Added + +- Added asset decimals field. + +# 1.4.0 + +# Added + +- Added support for Algorand Standardized Assets (ASA) +- Added support for Algorand Smart Contracts (ASC) + - Added support for Hashed Time Lock Contract (HTLC) + - Added support for Split contract +- Added support for Group Transactions +- Added support for leases + +# 1.3.1 + +## Changed + +- msgpack lib was replaced with the official https://github.com/msgpack/msgpack-javascript + +## Fixed + +- algod.transactionById returns the note as Uint8Array and not as base64 + +# 1.3.0 + +## Added + +- Support for key registration transactions +- Support for flat fees +- Signing and verifying bytes + +## Fixed + +- deleteMultisig uses post instead of get +- "MultiSig" standardized to "Multisig" + +# 1.2.2 + +## Added + +- Support for Optional Parameters for GetTransactionsByAddress + +# 1.2.1 + +## Added + +- Support for GetTransactionByID +- Handle the case of undeclared noteField + +# 1.2.0 + +## Added + +- Support of GenesisHash and Close Remainder To fields + +# 1.1.1 + +## Fixed + +- Bug Fix for Suggested Fee + +# 1.1.0 + +## Added + +- Support for multisignatures + +# 1.0.9 + +## Fixed + +- kmd can now sign transactions + +# 1.0.8 + +## Added + +- Support in more than one token for algodClient + +# 1.0.7 + +## Added + +- Support in new Suggested Fee scheme + +## Fixed + +- Now the client handles empty transactions list + +# 1.0.6 + +- Bug Fix + +# 1.0.5 + +## Added + +- Update to a newer msgpack version (Support 64bit numbers) +- Update algod client to convert b64 to buffer + +# 1.0.4 + +## Added + +- Support for arbitrary encoding and decoding of javascript objects + +# 1.0.3 + +## Added + +- Support for transaction information in algodClient + +# 1.0.2 + +## Added -# v2.1.1 +- Support for "genesis ID" field in transactions diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..966aa2c --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant 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, 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 +conduct@algorand.com. +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.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 0000000..1d44239 --- /dev/null +++ b/FAQ.md @@ -0,0 +1,21 @@ +# Frequently Asked Questions + +## Where did the `dist` folder go? + +Starting with version 1.9.0, a minified browser bundle can be obtained from npm CDNs such as [unpkg](https://unpkg.com/) or [jsDelivr](https://www.jsdelivr.com/). The link can be found in the [README.md](README.md). + +In prior versions, the browser bundles were made available in the `dist` directory. In order to access those, look in the `dist` folder on the version's GitHub tag, e.g. https://github.com/algorand/js-algorand-sdk/tree/v1.8.1/dist. + +## Can I host the browser bundle myself? + +Yes! If you would instead prefer to host the package yourself, a minified browser bundle can be found in the `dist/browser/` folder of the published npm package starting with version 1.9.0. + +## How do I generate the SRI hash of `algosdk.min.js`? + +The SRI hash of `algosdk.min.js` is the base64 string after `sha384-` in the `integrity` attribute of the ` +``` + +or + +```html + +``` + +Information about hosting the package for yourself, finding the browser bundles of previous versions, and computing the SRI hash is [available here](FAQ.md). + +## Quick Start + +```javascript +const token = 'Your algod API token'; +const server = 'http://127.0.0.1'; +const port = 8080; +const client = new algosdk.Algodv2(token, server, port); + +(async () => { + console.log(await client.status().do()); +})().catch((e) => { + console.log(e); +}); +``` + +## Documentation + +Documentation for this SDK is available here: https://algorand.github.io/js-algorand-sdk/. Additional resources are available on https://developer.algorand.org. + +## Examples + +Running examples requires access to a running node. Follow the instructions in Algorand's [developer resources](https://developer.algorand.org/docs/run-a-node/setup/install/) to install a node on your computer. + +**As portions of the codebase are written in TypeScript, example files cannot be run directly using `node`**. Please refer to the instructions described in the [examples/README.md](examples/README.md) file for more information regarding running the examples. + +## SDK Development + +### Building + +To build a new version of the library, run: + +```bash +npm run build +``` + +### Generating Documentation + +To generate the documentation website, run: + +```bash +npm run docs +``` + +The static website will be located in the `docs/` directory. + +### Testing + +We have two test suites: mocha tests in this repo, and the Algorand SDK test suite from https://github.com/algorand/algorand-sdk-testing. + +#### Node.js + +To run the mocha tests in Node.js, run: + +```bash +npm test +``` + +To run the SDK test suite in Node.js, run: + +```bash +make docker-test +``` + +#### Browsers + +The test suites can also run in browsers. To do so, set the environment variable `TEST_BROWSER` to +one of our supported browsers. Currently we support testing in `chrome` and `firefox`. When +`TEST_BROWSER` is set, the mocha and SDK test suites will run in that browser. + +For example, to run mocha tests in Chrome: + +```bash +TEST_BROWSER=chrome npm test +``` + +And to run SDK tests in Firefox: + +```bash +TEST_BROWSER=firefox make docker-test +``` + +### Code Style + +This project enforces a modified version of the [Airbnb code style](https://github.com/airbnb/javascript). + +We've setup linters and formatters to help catch errors and improve the development experience: + +- [Prettier](https://prettier.io/) – ensures that code is formatted in a readable way. +- [ESLint](https://eslint.org/) — checks code for antipatterns as well as formatting. + +> If using the Visual Studio Code editor with the [recommended extensions](.vscode/extensions.json), ESLint errors should be highlighted in red and the Prettier extension should format code on every save. + +#### Precommit Hook + +The linters and formatters listed above should run automatically on each commit to catch errors early and save CI running time. + +## License + +js-algorand-sdk is licensed under an MIT license. See the [LICENSE](https://github.com/algorand/js-algorand-sdk/blob/master/LICENSE) file for details. diff --git a/examples/.eslintrc.json b/examples/.eslintrc.json new file mode 100644 index 0000000..d5ba8f9 --- /dev/null +++ b/examples/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "rules": { + "no-console": "off" + } +} diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..d829bed --- /dev/null +++ b/examples/README.md @@ -0,0 +1,9 @@ +## Algorand JavaScript SDK Examples + +This directory contains examples of how to use the Algorand JavaScript SDK. + +Assuming a sandbox node is running locally, any example can be run with the following command: + +```sh + ts-node .ts +``` diff --git a/examples/accounts.ts b/examples/accounts.ts new file mode 100644 index 0000000..8491c08 --- /dev/null +++ b/examples/accounts.ts @@ -0,0 +1,119 @@ +/* eslint-disable import/extensions */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable no-promise-executor-return */ +/* eslint-disable no-console */ +import algosdk from '../src'; +import { getLocalAlgodClient, getLocalAccounts } from './utils'; + +async function main() { + const client = getLocalAlgodClient(); + const accounts = await getLocalAccounts(); + const suggestedParams = await client.getTransactionParams().do(); + + const acct1 = accounts[0]; + const acct2 = accounts[1]; + + // example: ACCOUNT_RECOVER_MNEMONIC + // restore 25-word mnemonic from a string + // Note the mnemonic should _never_ appear in your source code + const mnemonic = + 'creek phrase island true then hope employ veteran rapid hurdle above liberty tissue connect alcohol timber idle ten frog bulb embody crunch taxi abstract month'; + const recoveredAccount = algosdk.mnemonicToSecretKey(mnemonic); + console.log('Recovered mnemonic account: ', recoveredAccount.addr); + // example: ACCOUNT_RECOVER_MNEMONIC + + const funder = accounts[0]; + + // example: MULTISIG_CREATE + const signerAccounts: algosdk.Account[] = []; + signerAccounts.push(algosdk.generateAccount()); + signerAccounts.push(algosdk.generateAccount()); + signerAccounts.push(algosdk.generateAccount()); + + // multiSigParams is used when creating the address and when signing transactions + const multiSigParams = { + version: 1, + threshold: 2, + addrs: signerAccounts.map((a) => a.addr), + }; + const multisigAddr = algosdk.multisigAddress(multiSigParams); + + console.log('Created MultiSig Address: ', multisigAddr); + // example: MULTISIG_CREATE + + const fundMsigTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: funder.addr, + to: multisigAddr, + amount: 1_000_000, + suggestedParams, + }); + + await client.sendRawTransaction(fundMsigTxn.signTxn(funder.privateKey)).do(); + await algosdk.waitForConfirmation(client, fundMsigTxn.txID().toString(), 3); + + // example: MULTISIG_SIGN + const msigTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: multisigAddr, + to: funder.addr, + amount: 100, + suggestedParams, + }); + + // First signature uses signMultisigTransaction + const msigWithFirstSig = algosdk.signMultisigTransaction( + msigTxn, + multiSigParams, + signerAccounts[0].sk + ).blob; + + // Subsequent signatures use appendSignMultisigTransaction + const msigWithSecondSig = algosdk.appendSignMultisigTransaction( + msigWithFirstSig, + multiSigParams, + signerAccounts[1].sk + ).blob; + + await client.sendRawTransaction(msigWithSecondSig).do(); + await algosdk.waitForConfirmation(client, msigTxn.txID().toString(), 3); + // example: MULTISIG_SIGN + + // example: ACCOUNT_GENERATE + const generatedAccount = algosdk.generateAccount(); + const passphrase = algosdk.secretKeyToMnemonic(generatedAccount.sk); + console.log(`My address: ${generatedAccount.addr}`); + console.log(`My passphrase: ${passphrase}`); + // example: ACCOUNT_GENERATE + + // example: ACCOUNT_REKEY + // rekey the original account to the new signer via a payment transaction + // Note any transaction type can be used to rekey an account + const rekeyTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: acct1.addr, + to: acct1.addr, + amount: 0, + suggestedParams, + rekeyTo: acct2.addr, // set the rekeyTo field to the new signer + }); + + await client.sendRawTransaction(rekeyTxn.signTxn(acct1.privateKey)).do(); + await algosdk.waitForConfirmation(client, rekeyTxn.txID().toString(), 3); + + const acctInfo = await client.accountInformation(acct1.addr).do(); + + console.log(`Account Info: ${acctInfo} Auth Addr: ${acctInfo['auth-addr']}`); + // example: ACCOUNT_REKEY + + // the transaction is from originalAccount, but signed with newSigner private key + + const rekeyBack = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: acct1.addr, + to: acct1.addr, + amount: 0, + suggestedParams, + rekeyTo: acct1.addr, + }); + await client.sendRawTransaction(rekeyBack.signTxn(acct2.privateKey)).do(); + await algosdk.waitForConfirmation(client, rekeyBack.txID().toString(), 3); +} + +main(); diff --git a/examples/app.ts b/examples/app.ts new file mode 100644 index 0000000..aef7517 --- /dev/null +++ b/examples/app.ts @@ -0,0 +1,261 @@ +/* eslint-disable import/extensions */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable no-promise-executor-return */ +/* eslint-disable no-console */ +import fs from 'fs'; +import path from 'path'; +import { Buffer } from 'buffer'; +import { getLocalAlgodClient, getLocalAccounts, compileProgram } from './utils'; +import algosdk from '../src'; + +async function main() { + const algodClient = getLocalAlgodClient(); + const accounts = await getLocalAccounts(); + const creator = accounts[0]; + const suggestedParams = await algodClient.getTransactionParams().do(); + + // example: APP_SOURCE + // define TEAL source from string or from a file + const approvalProgram = fs.readFileSync( + path.join(__dirname, '/application/approval.teal'), + 'utf8' + ); + const clearProgram = fs.readFileSync( + path.join(__dirname, '/application/clear.teal'), + 'utf8' + ); + // example: APP_SOURCE + + // example: APP_COMPILE + const approvalCompileResp = await algodClient + .compile(Buffer.from(approvalProgram)) + .do(); + + const compiledApprovalProgram = new Uint8Array( + Buffer.from(approvalCompileResp.result, 'base64') + ); + + const clearCompileResp = await algodClient + .compile(Buffer.from(clearProgram)) + .do(); + + const compiledClearProgram = new Uint8Array( + Buffer.from(clearCompileResp.result, 'base64') + ); + // example: APP_COMPILE + + // example: APP_SCHEMA + // define uint64s and byteslices stored in global/local storage + const numGlobalByteSlices = 1; + const numGlobalInts = 1; + const numLocalByteSlices = 1; + const numLocalInts = 1; + // example: APP_SCHEMA + + // example: APP_CREATE + const appCreateTxn = algosdk.makeApplicationCreateTxnFromObject({ + from: creator.addr, + approvalProgram: compiledApprovalProgram, + clearProgram: compiledClearProgram, + numGlobalByteSlices, + numGlobalInts, + numLocalByteSlices, + numLocalInts, + suggestedParams, + onComplete: algosdk.OnApplicationComplete.NoOpOC, + }); + + // Sign and send + await algodClient + .sendRawTransaction(appCreateTxn.signTxn(creator.privateKey)) + .do(); + const result = await algosdk.waitForConfirmation( + algodClient, + appCreateTxn.txID().toString(), + 3 + ); + // Grab app id from confirmed transaction result + const appId = result['application-index']; + console.log(`Created app with index: ${appId}`); + // example: APP_CREATE + + const caller = accounts[1]; + + // example: APP_OPTIN + const appOptInTxn = algosdk.makeApplicationOptInTxnFromObject({ + from: caller.addr, + appIndex: appId, + suggestedParams, + }); + + await algodClient + .sendRawTransaction(appOptInTxn.signTxn(caller.privateKey)) + .do(); + await algosdk.waitForConfirmation( + algodClient, + appOptInTxn.txID().toString(), + 3 + ); + // example: APP_OPTIN + + // example: APP_NOOP + const appNoOpTxn = algosdk.makeApplicationNoOpTxnFromObject({ + from: caller.addr, + appIndex: appId, + suggestedParams, + }); + + await algodClient + .sendRawTransaction(appNoOpTxn.signTxn(caller.privateKey)) + .do(); + await algosdk.waitForConfirmation( + algodClient, + appNoOpTxn.txID().toString(), + 3 + ); + // example: APP_NOOP + + const anotherCaller = accounts[2]; + + const anotherAppOptInTxn = algosdk.makeApplicationOptInTxnFromObject({ + from: anotherCaller.addr, + appIndex: appId, + suggestedParams, + }); + + await algodClient + .sendRawTransaction(anotherAppOptInTxn.signTxn(anotherCaller.privateKey)) + .do(); + await algosdk.waitForConfirmation( + algodClient, + anotherAppOptInTxn.txID().toString(), + 3 + ); + + // example: APP_CALL + const now = new Date().toString(); + const simpleAddTxn = algosdk.makeApplicationNoOpTxnFromObject({ + from: caller.addr, + suggestedParams, + appIndex: appId, + appArgs: [new Uint8Array(Buffer.from(now))], + }); + + await algodClient + .sendRawTransaction(simpleAddTxn.signTxn(caller.privateKey)) + .do(); + await algosdk.waitForConfirmation( + algodClient, + simpleAddTxn.txID().toString(), + 3 + ); + // example: APP_CALL + + // example: APP_READ_STATE + const appInfo = await algodClient.getApplicationByID(appId).do(); + const globalState = appInfo.params['global-state'][0]; + console.log(`Raw global state - ${JSON.stringify(globalState)}`); + + // decode b64 string key with Buffer + const globalKey = Buffer.from(globalState.key, 'base64').toString(); + // decode b64 address value with encodeAddress and Buffer + const globalValue = algosdk.encodeAddress( + Buffer.from(globalState.value.bytes, 'base64') + ); + + console.log(`Decoded global state - ${globalKey}: ${globalValue}`); + + const accountAppInfo = await algodClient + .accountApplicationInformation(caller.addr, appId) + .do(); + + const localState = accountAppInfo['app-local-state']['key-value'][0]; + console.log(`Raw local state - ${JSON.stringify(localState)}`); + + // decode b64 string key with Buffer + const localKey = Buffer.from(localState.key, 'base64').toString(); + // get uint value directly + const localValue = localState.value.uint; + + console.log(`Decoded local state - ${localKey}: ${localValue}`); + // example: APP_READ_STATE + + // example: APP_CLOSEOUT + const appCloseOutTxn = algosdk.makeApplicationCloseOutTxnFromObject({ + from: caller.addr, + appIndex: appId, + suggestedParams, + }); + + await algodClient + .sendRawTransaction(appCloseOutTxn.signTxn(caller.privateKey)) + .do(); + await algosdk.waitForConfirmation( + algodClient, + appCloseOutTxn.txID().toString(), + 3 + ); + // example: APP_CLOSEOUT + + // example: APP_UPDATE + const newProgram = fs.readFileSync( + path.join(__dirname, '/application/approval_refactored.teal'), + 'utf8' + ); + const compiledNewProgram = await compileProgram(algodClient, newProgram); + + const appUpdateTxn = algosdk.makeApplicationUpdateTxnFromObject({ + from: creator.addr, + suggestedParams, + appIndex: appId, + // updates must define both approval and clear programs, even if unchanged + approvalProgram: compiledNewProgram, + clearProgram: compiledClearProgram, + }); + + await algodClient + .sendRawTransaction(appUpdateTxn.signTxn(creator.privateKey)) + .do(); + await algosdk.waitForConfirmation( + algodClient, + appUpdateTxn.txID().toString(), + 3 + ); + // example: APP_UPDATE + + // example: APP_CLEAR + const appClearTxn = algosdk.makeApplicationClearStateTxnFromObject({ + from: anotherCaller.addr, + suggestedParams, + appIndex: appId, + }); + + await algodClient + .sendRawTransaction(appClearTxn.signTxn(anotherCaller.privateKey)) + .do(); + await algosdk.waitForConfirmation( + algodClient, + appClearTxn.txID().toString(), + 3 + ); + // example: APP_CLEAR + + // example: APP_DELETE + const appDeleteTxn = algosdk.makeApplicationDeleteTxnFromObject({ + from: creator.addr, + suggestedParams, + appIndex: appId, + }); + + await algodClient + .sendRawTransaction(appDeleteTxn.signTxn(creator.privateKey)) + .do(); + await algosdk.waitForConfirmation( + algodClient, + appDeleteTxn.txID().toString(), + 3 + ); + // example: APP_DELETE +} + +main(); diff --git a/examples/application/approval.teal b/examples/application/approval.teal new file mode 100644 index 0000000..eabde62 --- /dev/null +++ b/examples/application/approval.teal @@ -0,0 +1,100 @@ +#pragma version 4 +// Handle each possible OnCompletion type. We don't have to worry about +// handling ClearState, because the ClearStateProgram will execute in that +// case, not the ApprovalProgram. +txn ApplicationID +int 0 +== +bnz handle_approve + +txn OnCompletion +int NoOp +== +bnz handle_noop + +txn OnCompletion +int OptIn +== +bnz handle_approve + +txn OnCompletion +int CloseOut +== +bnz handle_closeout + +txn OnCompletion +int UpdateApplication +== +bnz handle_updateapp + +txn OnCompletion +int DeleteApplication +== +bnz handle_deleteapp + +// Unexpected OnCompletion value. Should be unreachable. +err + +handle_noop: +// Handle NoOp + +// read global state +byte "counter" +dup +app_global_get + +// increment the value +int 1 ++ + +// store to scratch space +dup +store 0 + +// update global state +app_global_put + +// read local state for sender +int 0 +byte "counter" +app_local_get + +// increment the value +int 1 ++ +store 1 + +// update local state for sender +int 0 +byte "counter" +load 1 +app_local_put + +// load return value as approval +load 0 +return + + +handle_closeout: +// Handle CloseOut +//approval +int 1 +return + +handle_deleteapp: +// Check for creator +global CreatorAddress +txn Sender +== +return + +handle_updateapp: +// Check for creator +global CreatorAddress +txn Sender +== +return + +handle_approve: +int 1 +return diff --git a/examples/application/approval_refactored.teal b/examples/application/approval_refactored.teal new file mode 100644 index 0000000..1af5a7e --- /dev/null +++ b/examples/application/approval_refactored.teal @@ -0,0 +1,107 @@ +#pragma version 4 +// Handle each possible OnCompletion type. We don't have to worry about +// handling ClearState, because the ClearStateProgram will execute in that +// case, not the ApprovalProgram. + +txn ApplicationID +int 0 +== +bnz handle_approve + +txn OnCompletion +int NoOp +== +bnz handle_noop + +txn OnCompletion +int OptIn +== +bnz handle_approve + +txn OnCompletion +int CloseOut +== +bnz handle_closeout + +txn OnCompletion +int UpdateApplication +== +bnz handle_updateapp + +txn OnCompletion +int DeleteApplication +== +bnz handle_deleteapp + +// Unexpected OnCompletion value. Should be unreachable. +err + +handle_noop: +// Handle NoOp + +// read global state +byte "counter" +dup +app_global_get + +// increment the value +int 1 ++ + +// store to scratch space +dup +store 0 + +// update global state +app_global_put + +// read local state for sender +int 0 +byte "counter" +app_local_get + +// increment the value +int 1 ++ +store 1 + +// update local state for sender +// update "counter" +int 0 +byte "counter" +load 1 +app_local_put + +// update "timestamp" +int 0 +byte "timestamp" +txn ApplicationArgs 0 +app_local_put + +// load return value as approval +load 0 +return + +handle_closeout: +// Handle CloseOut +//approval +int 1 +return + +handle_deleteapp: +// Check for creator +global CreatorAddress +txn Sender +== +return + +handle_updateapp: +// Check for creator +global CreatorAddress +txn Sender +== +return + +handle_approve: +int 1 +return \ No newline at end of file diff --git a/examples/application/clear.teal b/examples/application/clear.teal new file mode 100644 index 0000000..d793651 --- /dev/null +++ b/examples/application/clear.teal @@ -0,0 +1,3 @@ +#pragma version 4 +int 1 +return \ No newline at end of file diff --git a/examples/asa.ts b/examples/asa.ts new file mode 100644 index 0000000..6eb0353 --- /dev/null +++ b/examples/asa.ts @@ -0,0 +1,198 @@ +/* eslint-disable import/extensions */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable no-promise-executor-return */ +/* eslint-disable no-console */ +import algosdk from '../src'; +import { + getLocalAlgodClient, + getLocalAccounts, + getLocalIndexerClient, +} from './utils'; + +async function main() { + const algodClient = getLocalAlgodClient(); + const accounts = await getLocalAccounts(); + const creator = accounts[0]; + + // example: ASSET_CREATE + const suggestedParams = await algodClient.getTransactionParams().do(); + const txn = algosdk.makeAssetCreateTxnWithSuggestedParamsFromObject({ + from: creator.addr, + suggestedParams, + defaultFrozen: false, + unitName: 'rug', + assetName: 'Really Useful Gift', + manager: creator.addr, + reserve: creator.addr, + freeze: creator.addr, + clawback: creator.addr, + assetURL: 'http://path/to/my/asset/details', + total: 1000, + decimals: 0, + }); + + const signedTxn = txn.signTxn(creator.privateKey); + await algodClient.sendRawTransaction(signedTxn).do(); + const result = await algosdk.waitForConfirmation( + algodClient, + txn.txID().toString(), + 3 + ); + + const assetIndex = result['asset-index']; + console.log(`Asset ID created: ${assetIndex}`); + // example: ASSET_CREATE + + // example: ASSET_INFO + const assetInfo = await algodClient.getAssetByID(assetIndex).do(); + console.log(`Asset Name: ${assetInfo.params.name}`); + console.log(`Asset Params: ${assetInfo.params}`); + // example: ASSET_INFO + + await new Promise((f) => setTimeout(f, 45000)); // sleep to ensure indexer is caught up + + // example: INDEXER_LOOKUP_ASSET + const indexer = getLocalIndexerClient(); + const indexerAssetInfo = await indexer.lookupAssetByID(assetIndex).do(); + console.log('Indexer Asset Info:', indexerAssetInfo); + // example: INDEXER_LOOKUP_ASSET + + // example: ASSET_CONFIG + const manager = accounts[1]; + + const configTxn = algosdk.makeAssetConfigTxnWithSuggestedParamsFromObject({ + from: creator.addr, + manager: manager.addr, + freeze: manager.addr, + clawback: manager.addr, + reserve: undefined, + suggestedParams, + assetIndex, + // don't throw error if freeze, clawback, or manager are empty + strictEmptyAddressChecking: false, + }); + + const signedConfigTxn = configTxn.signTxn(creator.privateKey); + await algodClient.sendRawTransaction(signedConfigTxn).do(); + const configResult = await algosdk.waitForConfirmation( + algodClient, + txn.txID().toString(), + 3 + ); + console.log(`Result confirmed in round: ${configResult['confirmed-round']}`); + // example: ASSET_CONFIG + + const receiver = accounts[2]; + // example: ASSET_OPTIN + + // opt-in is simply a 0 amount transfer of the asset to oneself + const optInTxn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ + from: receiver.addr, + to: receiver.addr, + suggestedParams, + assetIndex, + amount: 0, + }); + + const signedOptInTxn = optInTxn.signTxn(receiver.privateKey); + await algodClient.sendRawTransaction(signedOptInTxn).do(); + await algosdk.waitForConfirmation(algodClient, optInTxn.txID().toString(), 3); + // example: ASSET_OPTIN + + // example: ASSET_XFER + const xferTxn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ + from: creator.addr, + to: receiver.addr, + suggestedParams, + assetIndex, + amount: 1, + }); + + const signedXferTxn = xferTxn.signTxn(creator.privateKey); + await algodClient.sendRawTransaction(signedXferTxn).do(); + await algosdk.waitForConfirmation(algodClient, xferTxn.txID().toString(), 3); + // example: ASSET_XFER + + // example: ASSET_FREEZE + const freezeTxn = algosdk.makeAssetFreezeTxnWithSuggestedParamsFromObject({ + from: manager.addr, + suggestedParams, + assetIndex, + // freezeState: false would unfreeze the account's asset holding + freezeState: true, + // freezeTarget is the account that is being frozen or unfrozen + freezeTarget: receiver.addr, + }); + + const signedFreezeTxn = freezeTxn.signTxn(manager.privateKey); + await algodClient.sendRawTransaction(signedFreezeTxn).do(); + await algosdk.waitForConfirmation( + algodClient, + freezeTxn.txID().toString(), + 3 + ); + // example: ASSET_FREEZE + + // example: ASSET_CLAWBACK + const clawbackTxn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject( + { + from: manager.addr, + to: creator.addr, + // revocationTarget is the account that is being clawed back from + revocationTarget: receiver.addr, + suggestedParams, + assetIndex, + amount: 1, + } + ); + + const signedClawbackTxn = clawbackTxn.signTxn(manager.privateKey); + await algodClient.sendRawTransaction(signedClawbackTxn).do(); + await algosdk.waitForConfirmation( + algodClient, + clawbackTxn.txID().toString(), + 3 + ); + // example: ASSET_CLAWBACK + + // example: ASSET_OPT_OUT + + // opt-out is an amount transfer with the `closeRemainderTo` field set to + // any account that can receive the asset. + // note that closing to the asset creator will always succeed + const optOutTxn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ + from: receiver.addr, + to: creator.addr, + closeRemainderTo: creator.addr, + suggestedParams, + assetIndex, + amount: 0, + }); + + const signedOptOutTxn = optOutTxn.signTxn(receiver.privateKey); + await algodClient.sendRawTransaction(signedOptOutTxn).do(); + await algosdk.waitForConfirmation( + algodClient, + optOutTxn.txID().toString(), + 3 + ); + // example: ASSET_OPT_OUT + + // example: ASSET_DELETE + const deleteTxn = algosdk.makeAssetDestroyTxnWithSuggestedParamsFromObject({ + from: manager.addr, + suggestedParams, + assetIndex, + }); + + const signedDeleteTxn = deleteTxn.signTxn(manager.privateKey); + await algodClient.sendRawTransaction(signedDeleteTxn).do(); + await algosdk.waitForConfirmation( + algodClient, + deleteTxn.txID().toString(), + 3 + ); + // example: ASSET_DELETE +} + +main(); diff --git a/examples/atc.ts b/examples/atc.ts new file mode 100644 index 0000000..eccac71 --- /dev/null +++ b/examples/atc.ts @@ -0,0 +1,120 @@ +/* eslint-disable import/extensions */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable no-promise-executor-return */ +/* eslint-disable no-console */ +import fs from 'fs'; +import path from 'path'; +import { Buffer } from 'buffer'; +import algosdk from '../src'; +import { getLocalAlgodClient, getLocalAccounts, compileProgram } from './utils'; + +async function main() { + const client = getLocalAlgodClient(); + const accounts = await getLocalAccounts(); + + const sender = accounts[0]; + const suggestedParams = await client.getTransactionParams().do(); + + const approvalProgram = fs.readFileSync( + path.join(__dirname, '/calculator/approval.teal'), + 'utf8' + ); + const clearProgram = fs.readFileSync( + path.join(__dirname, '/calculator/clear.teal'), + 'utf8' + ); + + const compiledApprovalProgram = await compileProgram(client, approvalProgram); + const compiledClearProgram = await compileProgram(client, clearProgram); + + const createTxn = algosdk.makeApplicationCreateTxnFromObject({ + from: sender.addr, + suggestedParams, + onComplete: algosdk.OnApplicationComplete.NoOpOC, + approvalProgram: compiledApprovalProgram, + clearProgram: compiledClearProgram, + numGlobalByteSlices: 0, + numGlobalInts: 0, + numLocalByteSlices: 0, + numLocalInts: 0, + appArgs: [], + }); + + await client.sendRawTransaction(createTxn.signTxn(sender.privateKey)).do(); + const response = await algosdk.waitForConfirmation( + client, + createTxn.txID().toString(), + 3 + ); + const appIndex = response['application-index']; + + // example: ATC_CREATE + const atc = new algosdk.AtomicTransactionComposer(); + // example: ATC_CREATE + + // example: ATC_CONTRACT_INIT + const abi = JSON.parse( + fs.readFileSync(path.join(__dirname, '/calculator/contract.json'), 'utf8') + ); + const contract = new algosdk.ABIContract(abi); + // example: ATC_CONTRACT_INIT + + // example: ATC_ADD_TRANSACTION + // construct a transaction + const paymentTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: sender.addr, + suggestedParams, + to: sender.addr, + amount: 1000, + }); + + // add the transaction to the ATC with a signer + atc.addTransaction({ txn: paymentTxn, signer: sender.signer }); + // example: ATC_ADD_TRANSACTION + + // example: ATC_ADD_METHOD_CALL + atc.addMethodCall({ + appID: appIndex, + method: contract.getMethodByName('add'), + methodArgs: [1, 2], + sender: sender.addr, + signer: sender.signer, + suggestedParams, + }); + // example: ATC_ADD_METHOD_CALL + + // example: ATC_RESULTS + const result = await atc.execute(client, 4); + for (const mr of result.methodResults) { + console.log(`${mr.returnValue}`); + } + // example: ATC_RESULTS + + // made up method + const boxAccessorMethod = new algosdk.ABIMethod({ + name: 'box_accessor', + args: [], + returns: { type: 'void' }, + }); + + // example: ATC_BOX_REF + const boxATC = new algosdk.AtomicTransactionComposer(); + const boxKey = new Uint8Array(Buffer.from('key')); + boxATC.addMethodCall({ + appID: appIndex, + method: boxAccessorMethod, + methodArgs: [], + boxes: [ + { + appIndex: 0, + name: boxKey, + }, + ], + sender: sender.addr, + signer: sender.signer, + suggestedParams, + }); + // example: ATC_BOX_REF +} + +main(); diff --git a/examples/atomics.ts b/examples/atomics.ts new file mode 100644 index 0000000..ed4b919 --- /dev/null +++ b/examples/atomics.ts @@ -0,0 +1,68 @@ +/* eslint-disable import/no-unresolved */ +/* eslint-disable no-promise-executor-return */ +/* eslint-disable no-console */ +import algosdk from '../src'; +// eslint-disable-next-line import/extensions +import { getLocalAlgodClient, getLocalAccounts } from './utils'; + +async function main() { + const client = getLocalAlgodClient(); + const accounts = await getLocalAccounts(); + + const acct1 = accounts[0]; + const acct2 = accounts[1]; + + // example: ATOMIC_CREATE_TXNS + const suggestedParams = await client.getTransactionParams().do(); + + const alicesTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: acct1.addr, + to: acct2.addr, + amount: 1e6, + suggestedParams, + }); + + const bobsTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: acct2.addr, + to: acct1.addr, + amount: 1e6, + suggestedParams, + }); + // example: ATOMIC_CREATE_TXNS + + // example: ATOMIC_GROUP_TXNS + const txnArray = [alicesTxn, bobsTxn]; + // assignGroupID returns the same txns with the group ID set + const txnGroup = algosdk.assignGroupID(txnArray); + // example: ATOMIC_GROUP_TXNS + + // example: ATOMIC_GROUP_SIGN + const alicesSignedTxn = txnGroup[0].signTxn(acct1.privateKey); + const bobsSignedTxn = txnGroup[1].signTxn(acct2.privateKey); + // example: ATOMIC_GROUP_SIGN + + // example: ATOMIC_GROUP_ASSEMBLE + const signedTxns = [alicesSignedTxn, bobsSignedTxn]; + // example: ATOMIC_GROUP_ASSEMBLE + + // example: ATOMIC_GROUP_SEND + await client.sendRawTransaction(signedTxns).do(); + await algosdk.waitForConfirmation(client, alicesTxn.txID().toString(), 3); + // example: ATOMIC_GROUP_SEND + + // example: CONST_MIN_FEE + const minFee = algosdk.ALGORAND_MIN_TX_FEE; + // example: CONST_MIN_FEE + + // example: TRANSACTION_FEE_OVERRIDE + const sp = await client.getTransactionParams().do(); + sp.fee = 2 * minFee; + sp.flatFee = true; + // example: TRANSACTION_FEE_OVERRIDE + + // example: SP_MIN_FEE + // Not supported because getTransactionParams erases the information + // example: SP_MIN_FEE +} + +main(); diff --git a/examples/block_fetcher/index.ts b/examples/block_fetcher/index.ts new file mode 100644 index 0000000..695c732 --- /dev/null +++ b/examples/block_fetcher/index.ts @@ -0,0 +1,85 @@ +/* eslint-disable no-await-in-loop */ +/* eslint-disable guard-for-in */ +import algosdk from '../../src'; + +// Validate all TxID +const validate = false; + +// Algod Node +const token = 'a'.repeat(64); +const address = 'http://127.0.0.1'; +const port = 8080; +const client = new algosdk.Algodv2(token, address, port); + +// Recursively remove all null values from object +function removeNulls(obj) { + for (const key in obj) { + if (obj[key] === null) { + // eslint-disable-next-line no-param-reassign + delete obj[key]; + } else if (typeof obj[key] === 'object') { + removeNulls(obj[key]); + } + } +} + +(async () => { + // Retrieve current status + let status = await client.status().do(); + + while (true) { + // Get latest round number + let lastRound = status['last-round']; + console.log(`Round: ${lastRound}`); + + // Fetch block + const round = await client.block(lastRound).do(); + const { block } = round; + const { txns } = block; + + // For all transactions in the block reconstruct them + // into Transaction objects and calculate their TxID + for (const t in txns) { + const tx = txns[t]; + const { txn } = txns[t]; + + // Skip StateProofs + if (txn.type === 'stpf') continue; + + // Remove nulls (mainly where an appl txn contains a null app arg) + removeNulls(txn); + + // Use Genesis Hash and Genesis ID from the block + const { gh } = block; + let { gen } = block; + + // Unset gen if `hgi` isn't set + if (!tx.hgi) gen = null; + + // Construct Transaction + const transaction = algosdk.Transaction.from_obj_for_encoding({ + ...txn, + gh, + gen, + }); + const txid = transaction.txID(); + + // If set to true, validate the TxID exists against the node + // !! Don't run on a public endpoint, you'll probably get rate limited !! + if (validate) { + try { + await client.pendingTransactionInformation(txid).do(); + console.log(`${txid}\t Exists`); + } catch (e) { + console.log(`${txid}\t Doesn't Exist`); + } + } else { + console.log(txid); + } + } + + // Wait for next round + status = await client.statusAfterBlock(lastRound).do(); + lastRound = status['last-round']; + } +})(); diff --git a/examples/calculator/approval.teal b/examples/calculator/approval.teal new file mode 100644 index 0000000..32acbb8 --- /dev/null +++ b/examples/calculator/approval.teal @@ -0,0 +1,181 @@ +#pragma version 8 +intcblock 0 1 +bytecblock 0x151f7c75 +txn NumAppArgs +intc_0 // 0 +== +bnz main_l10 +txna ApplicationArgs 0 +pushbytes 0xfe6bdf69 // "add(uint64,uint64)uint64" +== +bnz main_l9 +txna ApplicationArgs 0 +pushbytes 0xe2f188c5 // "mul(uint64,uint64)uint64" +== +bnz main_l8 +txna ApplicationArgs 0 +pushbytes 0x78b488b7 // "sub(uint64,uint64)uint64" +== +bnz main_l7 +txna ApplicationArgs 0 +pushbytes 0x16e80f08 // "div(uint64,uint64)uint64" +== +bnz main_l6 +err +main_l6: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 9 +txna ApplicationArgs 2 +btoi +store 10 +load 9 +load 10 +callsub div_3 +store 11 +bytec_0 // 0x151f7c75 +load 11 +itob +concat +log +intc_1 // 1 +return +main_l7: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 6 +txna ApplicationArgs 2 +btoi +store 7 +load 6 +load 7 +callsub sub_2 +store 8 +bytec_0 // 0x151f7c75 +load 8 +itob +concat +log +intc_1 // 1 +return +main_l8: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 3 +txna ApplicationArgs 2 +btoi +store 4 +load 3 +load 4 +callsub mul_1 +store 5 +bytec_0 // 0x151f7c75 +load 5 +itob +concat +log +intc_1 // 1 +return +main_l9: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 0 +txna ApplicationArgs 2 +btoi +store 1 +load 0 +load 1 +callsub add_0 +store 2 +bytec_0 // 0x151f7c75 +load 2 +itob +concat +log +intc_1 // 1 +return +main_l10: +txn OnCompletion +intc_0 // NoOp +== +bnz main_l12 +err +main_l12: +txn ApplicationID +intc_0 // 0 +== +assert +intc_1 // 1 +return + +// add +add_0: +proto 2 1 +intc_0 // 0 +frame_dig -2 +frame_dig -1 ++ +frame_bury 0 +retsub + +// mul +mul_1: +proto 2 1 +intc_0 // 0 +frame_dig -2 +frame_dig -1 +* +frame_bury 0 +retsub + +// sub +sub_2: +proto 2 1 +intc_0 // 0 +frame_dig -2 +frame_dig -1 +- +frame_bury 0 +retsub + +// div +div_3: +proto 2 1 +intc_0 // 0 +frame_dig -2 +frame_dig -1 +/ +frame_bury 0 +retsub \ No newline at end of file diff --git a/examples/calculator/clear.teal b/examples/calculator/clear.teal new file mode 100644 index 0000000..e741f0e --- /dev/null +++ b/examples/calculator/clear.teal @@ -0,0 +1,3 @@ +#pragma version 8 +pushint 0 // 0 +return \ No newline at end of file diff --git a/examples/calculator/contract.json b/examples/calculator/contract.json new file mode 100644 index 0000000..6fa3f9d --- /dev/null +++ b/examples/calculator/contract.json @@ -0,0 +1,74 @@ +{ + "name": "Calculator", + "methods": [ + { + "name": "add", + "args": [ + { + "type": "uint64", + "name": "a" + }, + { + "type": "uint64", + "name": "b" + } + ], + "returns": { + "type": "uint64" + }, + "desc": "Add a and b, return the result" + }, + { + "name": "mul", + "args": [ + { + "type": "uint64", + "name": "a" + }, + { + "type": "uint64", + "name": "b" + } + ], + "returns": { + "type": "uint64" + }, + "desc": "Multiply a and b, return the result" + }, + { + "name": "sub", + "args": [ + { + "type": "uint64", + "name": "a" + }, + { + "type": "uint64", + "name": "b" + } + ], + "returns": { + "type": "uint64" + }, + "desc": "Subtract b from a, return the result" + }, + { + "name": "div", + "args": [ + { + "type": "uint64", + "name": "a" + }, + { + "type": "uint64", + "name": "b" + } + ], + "returns": { + "type": "uint64" + }, + "desc": "Divide a by b, return the result" + } + ], + "networks": {} +} diff --git a/examples/codec.ts b/examples/codec.ts new file mode 100644 index 0000000..c74b9b6 --- /dev/null +++ b/examples/codec.ts @@ -0,0 +1,84 @@ +/* eslint-disable import/extensions */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable no-promise-executor-return */ +/* eslint-disable no-console */ +import { Buffer } from 'buffer'; +import algosdk from '../src'; +import { getLocalAlgodClient, getLocalAccounts } from './utils'; + +async function main() { + const client = getLocalAlgodClient(); + const accounts = await getLocalAccounts(); + const sender = accounts[0]; + const receiver = accounts[1]; + const suggestedParams = await client.getTransactionParams().do(); + + // example: CODEC_ADDRESS + const address = '4H5UNRBJ2Q6JENAXQ6HNTGKLKINP4J4VTQBEPK5F3I6RDICMZBPGNH6KD4'; + const pk = algosdk.decodeAddress(address); + const addr = algosdk.encodeAddress(pk.publicKey); + console.log(address, addr); + // example: CODEC_ADDRESS + + // example: CODEC_BASE64 + const b64Encoded = 'SGksIEknbSBkZWNvZGVkIGZyb20gYmFzZTY0'; + const b64Decoded = Buffer.from(b64Encoded, 'base64').toString(); + console.log(b64Encoded, b64Decoded); + // example: CODEC_BASE64 + + // example: CODEC_UINT64 + const int = 1337; + const encoded = algosdk.encodeUint64(int); + const safeDecoded = algosdk.decodeUint64(encoded, 'safe'); + const mixedDecoded = algosdk.decodeUint64(encoded, 'bigint'); + console.log(int, encoded, safeDecoded, mixedDecoded); + // example: CODEC_UINT64 + + // example: CODEC_TRANSACTION_UNSIGNED + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: sender.addr, + to: receiver.addr, + amount: 1e6, + suggestedParams, + }); + + const txnBytes = algosdk.encodeUnsignedTransaction(txn); + const txnB64 = Buffer.from(txnBytes).toString('base64'); + // ... + const restoredTxn = algosdk.decodeUnsignedTransaction( + Buffer.from(txnB64, 'base64') + ); + console.log(restoredTxn); + // example: CODEC_TRANSACTION_UNSIGNED + + // example: CODEC_TRANSACTION_SIGNED + const signedTxn = txn.signTxn(sender.privateKey); + const signedB64Txn = Buffer.from(signedTxn).toString('base64'); + const restoredSignedTxn = algosdk.decodeSignedTransaction( + Buffer.from(signedB64Txn, 'base64') + ); + console.log(restoredSignedTxn); + // example: CODEC_TRANSACTION_SIGNED + + // example: CODEC_ABI + const stringTupleCodec = algosdk.ABIType.from('(string,string)'); + + const stringTupleData = ['hello', 'world']; + const encodedTuple = stringTupleCodec.encode(stringTupleData); + console.log(Buffer.from(encodedTuple).toString('hex')); + + const decodedTuple = stringTupleCodec.decode(encodedTuple); + console.log(decodedTuple); // ['hello', 'world'] + + const uintArrayCodec = algosdk.ABIType.from('uint64[]'); + + const uintArrayData = [1, 2, 3, 4, 5]; + const encodedArray = uintArrayCodec.encode(uintArrayData); + console.log(Buffer.from(encodedArray).toString('hex')); + + const decodeArray = uintArrayCodec.decode(encodedArray); + console.log(decodeArray); // [1, 2, 3, 4, 5] + // example: CODEC_ABI +} + +main(); diff --git a/examples/debug.ts b/examples/debug.ts new file mode 100644 index 0000000..ed5515b --- /dev/null +++ b/examples/debug.ts @@ -0,0 +1,58 @@ +import fs from 'fs'; +import path from 'path'; +import algosdk from '../src'; +import { + deployCalculatorApp, + getLocalAccounts, + getLocalAlgodClient, +} from './utils'; + +async function main() { + const algodClient = getLocalAlgodClient(); + const accts = await getLocalAccounts(); + + const sender = accts[0]; + + const appId = await deployCalculatorApp(algodClient, sender); + + const contract = new algosdk.ABIContract( + JSON.parse( + fs.readFileSync(path.join(__dirname, '/calculator/contract.json'), 'utf8') + ) + ); + + // example: DEBUG_DRYRUN_DUMP + const suggestedParams = await algodClient.getTransactionParams().do(); + + const atc = new algosdk.AtomicTransactionComposer(); + atc.addMethodCall({ + appID: appId, + method: contract.getMethodByName('sub'), + methodArgs: [1, 2], + sender: sender.addr, + signer: sender.signer, + suggestedParams, + }); + + const signedTxns = (await atc.gatherSignatures()).map((stxn) => + algosdk.decodeSignedTransaction(stxn) + ); + + const dryrunRequest = await algosdk.createDryrun({ + client: algodClient, + txns: signedTxns, + }); + + console.log('Dryrun:', dryrunRequest.get_obj_for_encoding()); + // example: DEBUG_DRYRUN_DUMP + + // example: DEBUG_DRYRUN_SUBMIT + const dryrunResponse = await algodClient.dryrun(dryrunRequest).do(); + dryrunResponse.txns.forEach((txn) => { + console.log('Txn:', txn.txn); + console.log('Txn Results:', txn.txnresults); + }); + // example: DEBUG_DRYRUN_SUBMIT +} + +main(); diff --git a/examples/indexer.ts b/examples/indexer.ts new file mode 100644 index 0000000..3d62e3a --- /dev/null +++ b/examples/indexer.ts @@ -0,0 +1,95 @@ +/* eslint-disable import/extensions */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable no-promise-executor-return */ +/* eslint-disable no-console */ +import { Buffer } from 'buffer'; +import { + getLocalIndexerClient, + getLocalAccounts, + getLocalAlgodClient, +} from './utils'; +import algosdk from '../src'; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function getIndexerClient(): void { + // example: INDEXER_CREATE_CLIENT + const indexerToken = 'a'.repeat(64); + const indexerServer = 'http://localhost'; + const indexerPort = 8980; + + const indexerClient = new algosdk.Indexer( + indexerToken, + indexerServer, + indexerPort + ); + // example: INDEXER_CREATE_CLIENT + + console.log(indexerClient); +} + +async function main() { + const indexerClient = getLocalIndexerClient(); + + // example: INDEXER_SEARCH_MIN_AMOUNT + const transactionInfo = await indexerClient + .searchForTransactions() + .currencyGreaterThan(100) + .do(); + console.log(transactionInfo.transactions.map((t) => t.id)); + // example: INDEXER_SEARCH_MIN_AMOUNT + + // example: INDEXER_PAGINATE_RESULTS + let nextToken = ''; + + // nextToken will be undefined if we reached the last page + while (nextToken !== undefined) { + // eslint-disable-next-line no-await-in-loop + const response = await indexerClient + .searchForTransactions() + .limit(5) + .currencyGreaterThan(10) + .nextToken(nextToken) + .do(); + + nextToken = response['next-token']; + const txns = response.transactions; + if (txns.length > 0) + console.log(`Transaction IDs: ${response.transactions.map((t) => t.id)}`); + } + // example: INDEXER_PAGINATE_RESULTS + + const client = getLocalAlgodClient(); + const accounts = await getLocalAccounts(); + const suggestedParams = await client.getTransactionParams().do(); + + const sender = accounts[0]; + + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: sender.addr, + to: sender.addr, + amount: 1e6, + note: new Uint8Array(Buffer.from('Hello World!')), + suggestedParams, + }); + + await client.sendRawTransaction(txn.signTxn(sender.privateKey)).do(); + await algosdk.waitForConfirmation(client, txn.txID().toString(), 3); + + await new Promise((f) => setTimeout(f, 1000)); // sleep to ensure indexer is caught up + + // example: INDEXER_PREFIX_SEARCH + const txnsWithNotePrefix = await indexerClient + .searchForTransactions() + .notePrefix(Buffer.from('Hello')) + .do(); + console.log( + `Transactions with note prefix "Hello" ${JSON.stringify( + txnsWithNotePrefix, + undefined, + 2 + )}` + ); + // example: INDEXER_PREFIX_SEARCH +} + +main(); diff --git a/examples/kmd.ts b/examples/kmd.ts new file mode 100644 index 0000000..42d525f --- /dev/null +++ b/examples/kmd.ts @@ -0,0 +1,92 @@ +/* eslint-disable import/extensions */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable no-promise-executor-return */ +/* eslint-disable no-console */ +import algosdk from '../src'; +import { getLocalKmdClient } from './utils'; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function getKmdClient() { + // example: KMD_CREATE_CLIENT + const kmdToken = 'a'.repeat(64); + const kmdServer = 'http://localhost'; + const kmdPort = 4002; + + const kmdClient = new algosdk.Kmd(kmdToken, kmdServer, kmdPort); + // example: KMD_CREATE_CLIENT + console.log(kmdClient); +} + +async function main() { + const kmdClient = getLocalKmdClient(); + + // example: KMD_CREATE_WALLET + const walletName = 'testWallet1'; + const password = 'testpassword'; + // MDK is undefined since we are creating a completely new wallet + const masterDerivationKey = undefined; + const driver = 'sqlite'; + + const wallet = await kmdClient.createWallet( + walletName, + password, + masterDerivationKey, + driver + ); + const walletID = wallet.wallet.id; + console.log('Created wallet:', walletID); + // example: KMD_CREATE_WALLET + + // example: KMD_CREATE_ACCOUNT + // wallet handle is used to establish a session with the wallet + const wallethandle = ( + await kmdClient.initWalletHandle(walletID, 'testpassword') + ).wallet_handle_token; + console.log('Got wallet handle:', wallethandle); + + const { address } = await kmdClient.generateKey(wallethandle); + console.log('Created new account:', address); + // example: KMD_CREATE_ACCOUNT + + // example: KMD_EXPORT_ACCOUNT + const accountKey = await kmdClient.exportKey(wallethandle, password, address); + const accountMnemonic = algosdk.secretKeyToMnemonic(accountKey.private_key); + console.log('Account Mnemonic: ', accountMnemonic); + // example: KMD_EXPORT_ACCOUNT + + // example: KMD_IMPORT_ACCOUNT + const newAccount = algosdk.generateAccount(); + console.log('Account: ', newAccount.addr); + const importedAccount = await kmdClient.importKey( + wallethandle, + newAccount.sk + ); + console.log('Account successfully imported: ', importedAccount); + // example: KMD_IMPORT_ACCOUNT + + // example: KMD_RECOVER_WALLET + const exportedMDK = ( + await kmdClient.exportMasterDerivationKey(wallethandle, 'testpassword') + ).master_derivation_key; + const recoveredWallet = await kmdClient.createWallet( + 'testWallet2', + 'testpassword', + exportedMDK, + 'sqlite' + ); + const recoeveredWalletID = recoveredWallet.wallet.id; + + console.log('Created wallet: ', recoeveredWalletID); + + const recoveredWalletHandle = ( + await kmdClient.initWalletHandle(recoeveredWalletID, 'testpassword') + ).wallet_handle_token; + console.log('Got wallet handle: ', recoveredWalletHandle); + + const recoveredAddr = (await kmdClient.generateKey(recoveredWalletHandle)) + .address; + console.log('Recovered account: ', recoveredAddr); + // example: KMD_RECOVER_WALLET +} + +main(); diff --git a/examples/lsig.ts b/examples/lsig.ts new file mode 100644 index 0000000..33140ef --- /dev/null +++ b/examples/lsig.ts @@ -0,0 +1,95 @@ +/* eslint-disable import/extensions */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable no-promise-executor-return */ +/* eslint-disable no-console */ +import { Buffer } from 'buffer'; +import algosdk from '../src'; +import { getLocalAlgodClient, getLocalAccounts } from './utils'; + +async function main() { + const client = getLocalAlgodClient(); + const accounts = await getLocalAccounts(); + const funder = accounts[0]; + const suggestedParams = await client.getTransactionParams().do(); + + // example: LSIG_COMPILE + const smartSigSource = '#pragma version 8\nint 1\nreturn'; // approve everything + const result = await client.compile(Buffer.from(smartSigSource)).do(); + + // Hash is equivalent to the contract address + console.log('Hash: ', result.hash); + console.log('B64: ', result.result); + const b64program = result.result; + // example: LSIG_COMPILE + + // example: LSIG_INIT + let smartSig = new algosdk.LogicSig( + new Uint8Array(Buffer.from(b64program, 'base64')) + ); + // example: LSIG_INIT + + // example: LSIG_PASS_ARGS + const args = [Buffer.from('This is an argument!')]; + smartSig = new algosdk.LogicSig( + new Uint8Array(Buffer.from(b64program, 'base64')), + args + ); + // example: LSIG_PASS_ARGS + + const fundSmartSigTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: funder.addr, + to: smartSig.address(), + amount: 1e6, + suggestedParams, + }); + + await client + .sendRawTransaction(fundSmartSigTxn.signTxn(funder.privateKey)) + .do(); + await algosdk.waitForConfirmation( + client, + fundSmartSigTxn.txID().toString(), + 3 + ); + + // example: LSIG_SIGN_FULL + const smartSigTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: smartSig.address(), + to: funder.addr, + amount: 0.1e6, + suggestedParams, + }); + + const signedSmartSigTxn = algosdk.signLogicSigTransactionObject( + smartSigTxn, + smartSig + ); + + await client.sendRawTransaction(signedSmartSigTxn.blob).do(); + await algosdk.waitForConfirmation(client, signedSmartSigTxn.txID, 3); + // example: LSIG_SIGN_FULL + + // example: LSIG_DELEGATE_FULL + const userAccount = accounts[1]; + + // sign sig with userAccount so the program can send transactions from userAccount + smartSig.sign(userAccount.privateKey); + + const delegatedTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: userAccount.addr, + to: funder.addr, + amount: 0.1e6, + suggestedParams, + }); + + // use signLogicSigTransactionObject instead of the typical Transaction.signTxn function + const signedDelegatedTxn = algosdk.signLogicSigTransactionObject( + delegatedTxn, + smartSig + ); + + await client.sendRawTransaction(signedDelegatedTxn.blob).do(); + await algosdk.waitForConfirmation(client, signedDelegatedTxn.txID, 3); + // example: LSIG_DELEGATE_FULL +} +main(); diff --git a/examples/lsig/sample_arg.teal b/examples/lsig/sample_arg.teal new file mode 100644 index 0000000..8f21008 --- /dev/null +++ b/examples/lsig/sample_arg.teal @@ -0,0 +1,5 @@ +#pragma version 5 +arg_0 +btoi +int 123 +== \ No newline at end of file diff --git a/examples/lsig/simple.teal b/examples/lsig/simple.teal new file mode 100644 index 0000000..d629865 --- /dev/null +++ b/examples/lsig/simple.teal @@ -0,0 +1,3 @@ +#pragma version 5 +int 1 +return \ No newline at end of file diff --git a/examples/overview.ts b/examples/overview.ts new file mode 100644 index 0000000..0bec407 --- /dev/null +++ b/examples/overview.ts @@ -0,0 +1,51 @@ +import { Buffer } from 'buffer'; +import algosdk from '../src'; +import { getLocalAccounts, getLocalAlgodClient } from './utils'; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function getAlgodClient() { + // example: ALGOD_CREATE_CLIENT + const algodToken = 'a'.repeat(64); + const algodServer = 'http://localhost'; + const algodPort = 4001; + + const algodClient = new algosdk.Algodv2(algodToken, algodServer, algodPort); + // example: ALGOD_CREATE_CLIENT + console.log(algodClient); +} + +async function main() { + const algodClient = getLocalAlgodClient(); + const accts = await getLocalAccounts(); + const acct = accts[0]; + const acct2 = accts[1]; + + // example: TRANSACTION_PAYMENT_CREATE + const suggestedParams = await algodClient.getTransactionParams().do(); + const ptxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: acct.addr, + suggestedParams, + to: acct2.addr, + amount: 10000, + note: new Uint8Array(Buffer.from('hello world')), + }); + // example: TRANSACTION_PAYMENT_CREATE + + // example: TRANSACTION_PAYMENT_SIGN + const signedTxn = ptxn.signTxn(acct.privateKey); + // example: TRANSACTION_PAYMENT_SIGN + + // example: TRANSACTION_PAYMENT_SUBMIT + const { txId } = await algodClient.sendRawTransaction(signedTxn).do(); + const result = await algosdk.waitForConfirmation(algodClient, txId, 4); + console.log(result); + console.log(`Transaction Information: ${result.txn}`); + console.log(`Decoded Note: ${Buffer.from(result.txn.txn.note).toString()}`); + // example: TRANSACTION_PAYMENT_SUBMIT + + // example: ALGOD_FETCH_ACCOUNT_INFO + const acctInfo = await algodClient.accountInformation(acct.addr).do(); + console.log(`Account balance: ${acctInfo.amount} microAlgos`); + // example: ALGOD_FETCH_ACCOUNT_INFO +} +main(); diff --git a/examples/participation.ts b/examples/participation.ts new file mode 100644 index 0000000..4cb3da6 --- /dev/null +++ b/examples/participation.ts @@ -0,0 +1,59 @@ +import algosdk from '../src'; +import { getLocalAlgodClient } from './utils'; + +async function main() { + const algodClient = getLocalAlgodClient(); + + // example: TRANSACTION_KEYREG_ONLINE_CREATE + // get suggested parameters + const params = await algodClient.getTransactionParams().do(); + + // Parent addr + const addr = 'MWAPNXBDFFD2V5KWXAHWKBO7FO4JN36VR4CIBDKDDE7WAUAGZIXM3QPJW4'; + // VRF public key + const selectionKey = 'LrpLhvzr+QpN/bivh6IPpOaKGbGzTTB5lJtVfixmmgk='; + // Voting pub key + const voteKey = 'G/lqTV6MKspW6J8wH2d8ZliZ5XZVZsruqSBJMwLwlmo='; + // State proof key + const stateProofKey = + 'RpUpNWfZMjZ1zOOjv3MF2tjO714jsBt0GKnNsw0ihJ4HSZwci+d9zvUi3i67LwFUJgjQ5Dz4zZgHgGduElnmSA=='; + + // sets up keys for 100000 rounds + const numRounds = 1e5; + + // dilution default is sqrt num rounds + const keyDilution = numRounds ** 0.5; + + // create transaction + const onlineKeyreg = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject( + { + from: addr, + voteKey, + selectionKey, + stateProofKey, + voteFirst: params.firstRound, + voteLast: params.firstRound + numRounds, + voteKeyDilution: keyDilution, + suggestedParams: params, + } + ); + + console.log(onlineKeyreg.get_obj_for_encoding()); + // example: TRANSACTION_KEYREG_ONLINE_CREATE + + // example: TRANSACTION_KEYREG_OFFLINE_CREATE + // get suggested parameters + const suggestedParams = await algodClient.getTransactionParams().do(); + // create keyreg transaction to take this account offline + const offlineKeyReg = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject( + { + from: addr, + suggestedParams, + nonParticipation: true, + } + ); + console.log(offlineKeyReg.get_obj_for_encoding()); + // example: TRANSACTION_KEYREG_OFFLINE_CREATE +} + +main(); diff --git a/examples/smoke_test.sh b/examples/smoke_test.sh new file mode 100755 index 0000000..405b7d9 --- /dev/null +++ b/examples/smoke_test.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +export ALGOD_PORT="60000" +export INDEXER_PORT="59999" +export KMD_PORT="60001" + +# Loop over all files in the directory +for file in *; do + # Check if the file ends with ".ts" + if [[ $file == *.ts ]]; then + # Check if the filename is not "utils.ts" + if [[ $file != "utils.ts" ]]; then + # Call the file using `ts-node` + ../node_modules/.bin/ts-node "$file" + # Check if the test failed + if [ $? -ne 0 ]; then + echo "Test failed, stopping script" + exit 1 + fi + fi + + fi +done diff --git a/examples/utils.ts b/examples/utils.ts new file mode 100644 index 0000000..4546e10 --- /dev/null +++ b/examples/utils.ts @@ -0,0 +1,136 @@ +import fs from 'fs'; +import path from 'path'; +import { Buffer } from 'buffer'; +import algosdk from '../src'; + +export async function compileProgram( + client: algosdk.Algodv2, + programSource: string +) { + const compileResponse = await client.compile(Buffer.from(programSource)).do(); + const compiledBytes = new Uint8Array( + Buffer.from(compileResponse.result, 'base64') + ); + return compiledBytes; +} + +export function getLocalKmdClient() { + const kmdToken = 'a'.repeat(64); + const kmdServer = 'http://localhost'; + const kmdPort = process.env.KMD_PORT || '4002'; + + const kmdClient = new algosdk.Kmd(kmdToken, kmdServer, kmdPort); + return kmdClient; +} + +export function getLocalIndexerClient() { + const indexerToken = 'a'.repeat(64); + const indexerServer = 'http://localhost'; + const indexerPort = process.env.INDEXER_PORT || '8980'; + + const indexerClient = new algosdk.Indexer( + indexerToken, + indexerServer, + indexerPort + ); + return indexerClient; +} + +export function getLocalAlgodClient() { + const algodToken = 'a'.repeat(64); + const algodServer = 'http://localhost'; + const algodPort = process.env.ALGOD_PORT || '4001'; + + const algodClient = new algosdk.Algodv2(algodToken, algodServer, algodPort); + return algodClient; +} + +export interface SandboxAccount { + addr: string; + privateKey: Uint8Array; + signer: algosdk.TransactionSigner; +} + +export async function getLocalAccounts(): Promise { + const kmdClient = getLocalKmdClient(); + + const wallets = await kmdClient.listWallets(); + + let walletId; + // eslint-disable-next-line no-restricted-syntax + for (const wallet of wallets.wallets) { + if (wallet.name === 'unencrypted-default-wallet') walletId = wallet.id; + } + + if (walletId === undefined) + throw Error('No wallet named: unencrypted-default-wallet'); + + const handleResp = await kmdClient.initWalletHandle(walletId, ''); + const handle = handleResp.wallet_handle_token; + + const addresses = await kmdClient.listKeys(handle); + // eslint-disable-next-line camelcase + const acctPromises: Promise<{ private_key: Buffer }>[] = []; + + // eslint-disable-next-line no-restricted-syntax + for (const addr of addresses.addresses) { + acctPromises.push(kmdClient.exportKey(handle, '', addr)); + } + const keys = await Promise.all(acctPromises); + + // Don't need to wait for it + kmdClient.releaseWalletHandle(handle); + + return keys.map((k) => { + const addr = algosdk.encodeAddress(k.private_key.slice(32)); + const acct = { sk: k.private_key, addr } as algosdk.Account; + const signer = algosdk.makeBasicAccountTransactionSigner(acct); + + return { + addr: acct.addr, + privateKey: acct.sk, + signer, + }; + }); +} + +export async function deployCalculatorApp( + algodClient: algosdk.Algodv2, + creator: SandboxAccount +): Promise { + const approvalProgram = fs.readFileSync( + path.join(__dirname, '/calculator/approval.teal'), + 'utf8' + ); + const clearProgram = fs.readFileSync( + path.join(__dirname, '/calculator/clear.teal'), + 'utf8' + ); + + const approvalBin = await compileProgram(algodClient, approvalProgram); + const clearBin = await compileProgram(algodClient, clearProgram); + const suggestedParams = await algodClient.getTransactionParams().do(); + const appCreateTxn = algosdk.makeApplicationCreateTxnFromObject({ + from: creator.addr, + approvalProgram: approvalBin, + clearProgram: clearBin, + numGlobalByteSlices: 0, + numGlobalInts: 0, + numLocalByteSlices: 0, + numLocalInts: 0, + suggestedParams, + onComplete: algosdk.OnApplicationComplete.NoOpOC, + }); + + await algodClient + .sendRawTransaction(appCreateTxn.signTxn(creator.privateKey)) + .do(); + + const result = await algosdk.waitForConfirmation( + algodClient, + appCreateTxn.txID().toString(), + 3 + ); + const appId = result['application-index']; + return appId; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9600668 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,14113 @@ +{ + "name": "algosdk", + "version": "7.7.5", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "algosdk", + "version": "7.7.5", + "license": "MIT", + "dependencies": { + "algo-msgpack-with-bigint": "^2.1.1", + "buffer": "^6.0.3", + "cross-fetch": "^3.1.5", + "hi-base32": "^0.5.1", + "js-sha256": "^0.9.0", + "js-sha3": "^0.8.0", + "js-sha512": "^0.8.0", + "json-bigint": "^1.0.0", + "tweetnacl": "^1.0.3", + "vlq": "^2.0.4" + }, + "devDependencies": { + "@types/json-bigint": "^1.0.0", + "@types/mocha": "^8.2.2", + "@typescript-eslint/eslint-plugin": "^4.26.1", + "@typescript-eslint/parser": "^4.26.1", + "assert": "^2.0.0", + "chromedriver": "^108.0.0", + "concurrently": "^6.2.0", + "cucumber": "^5.1.0", + "es-abstract": "^1.18.3", + "eslint": "^7.21.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-config-prettier": "^8.1.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-tsdoc": "^0.2.11", + "express": "^4.17.1", + "geckodriver": "^3.0.1", + "husky": "^4.3.8", + "lint-staged": "^10.5.4", + "mocha": "^9.0.0", + "mock-http-server": "^1.4.3", + "prettier": "2.2.1", + "selenium-webdriver": "^4.2.0", + "source-map-loader": "^2.0.2", + "ts-loader": "^9.3.1", + "ts-node": "^10.9.1", + "typedoc": "^0.23.8", + "typedoc-plugin-missing-exports": "^0.23.0", + "typedoc-plugin-rename-defaults": "^0.6.4", + "typescript": "^4.7.4", + "webpack": "^5.75.0", + "webpack-cli": "^5.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/polyfill": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.12.1.tgz", + "integrity": "sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==", + "deprecated": "🚨 This package has been deprecated in favor of separate inclusion of a polyfill and regenerator-runtime (when needed). See the @babel/polyfill docs (https://babeljs.io/docs/en/babel-polyfill) for more information.", + "dev": true, + "dependencies": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.4" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz", + "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==", + "dev": true + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.15.2.tgz", + "integrity": "sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.13.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", + "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@testim/chrome-version": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.3.tgz", + "integrity": "sha512-g697J3WxV/Zytemz8aTuKjTGYtta9+02kva3C1xc7KXB8GdbfE1akGJIsZLyY/FSh2QrnE+fiB7vmWU3XNcb6A==", + "dev": true + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.8.tgz", + "integrity": "sha512-LM6XwBhjZRls1qJGpiM/It09SntEwe9M0riXRfQ9s6XlJQG0JPGl92ET18LtGeYh/GuOtafIXqwZeqLOd0FNFQ==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/eslint": { + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", + "dev": true + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "dev": true + }, + "node_modules/@types/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-WW+0cfH3ovFN6ROV+p/Xfw36dT6s16hbXBYIG49PYw6+j6e+AkpqYccctgxwyicBmC8CZDBnPhOH94shFhXgHQ==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mocha": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", + "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "15.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", + "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.1.tgz", + "integrity": "sha512-aoIusj/8CR+xDWmZxARivZjbMBQTT9dImUtdZ8tVCVRXgBUuuZyM5Of5A9D9arQPxbi/0rlJLcuArclz/rCMJw==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "4.26.1", + "@typescript-eslint/scope-manager": "4.26.1", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "lodash": "^4.17.21", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^4.0.0", + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.1.tgz", + "integrity": "sha512-sQHBugRhrXzRCs9PaGg6rowie4i8s/iD/DpTB+EXte8OMDfdCG5TvO73XlO9Wc/zi0uyN4qOmX9hIjQEyhnbmQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.26.1", + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/typescript-estree": "4.26.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.26.1.tgz", + "integrity": "sha512-q7F3zSo/nU6YJpPJvQveVlIIzx9/wu75lr6oDbDzoeIRWxpoc/HQ43G4rmMoCc5my/3uSj2VEpg/D83LYZF5HQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "4.26.1", + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/typescript-estree": "4.26.1", + "debug": "^4.3.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.26.1.tgz", + "integrity": "sha512-TW1X2p62FQ8Rlne+WEShyd7ac2LA6o27S9i131W4NwDSfyeVlQWhw8ylldNNS8JG6oJB9Ha9Xyc+IUcqipvheQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/visitor-keys": "4.26.1" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.26.1.tgz", + "integrity": "sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg==", + "dev": true, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz", + "integrity": "sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/visitor-keys": "4.26.1", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.1.tgz", + "integrity": "sha512-IGouNSSd+6x/fHtYRyLOM6/C+QxMDzWlDtN41ea+flWuSF9g02iqcIlX8wM53JkfljoIjP0U+yp7SiTS1onEkw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.26.1", + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.5.tgz", + "integrity": "sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.5.tgz", + "integrity": "sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.5.tgz", + "integrity": "sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.5.tgz", + "integrity": "sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.5.tgz", + "integrity": "sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.5", + "@webassemblyjs/helper-api-error": "1.11.5", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.5.tgz", + "integrity": "sha512-oC4Qa0bNcqnjAowFn7MPCETQgDYytpsfvz4ujZz63Zu/a/v71HeCAAmZsgZ3YVKec3zSPYytG3/PrRCqbtcAvA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.5.tgz", + "integrity": "sha512-uEoThA1LN2NA+K3B9wDo3yKlBfVtC6rh0i4/6hvbz071E8gTNZD/pT0MsBf7MeD6KbApMSkaAK0XeKyOZC7CIA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.5.tgz", + "integrity": "sha512-37aGq6qVL8A8oPbPrSGMBcp38YZFXcHfiROflJn9jxSdSMMM5dS5P/9e2/TpaJuhE+wFrbukN2WI6Hw9MH5acg==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.5.tgz", + "integrity": "sha512-ajqrRSXaTJoPW+xmkfYN6l8VIeNnR4vBOTQO9HzR7IygoCcKWkICbKFbVTNMjMgMREqXEr0+2M6zukzM47ZUfQ==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.5.tgz", + "integrity": "sha512-WiOhulHKTZU5UPlRl53gHR8OxdGsSOxqfpqWeA2FmcwBMaoEdz6b2x2si3IwC9/fSPLfe8pBMRTHVMk5nlwnFQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.5.tgz", + "integrity": "sha512-C0p9D2fAu3Twwqvygvf42iGCQ4av8MFBLiTb+08SZ4cEdwzWx9QeAHDo1E2k+9s/0w1DM40oflJOpkZ8jW4HCQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/helper-wasm-section": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5", + "@webassemblyjs/wasm-opt": "1.11.5", + "@webassemblyjs/wasm-parser": "1.11.5", + "@webassemblyjs/wast-printer": "1.11.5" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.5.tgz", + "integrity": "sha512-14vteRlRjxLK9eSyYFvw1K8Vv+iPdZU0Aebk3j6oB8TQiQYuO6hj9s4d7qf6f2HJr2khzvNldAFG13CgdkAIfA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/ieee754": "1.11.5", + "@webassemblyjs/leb128": "1.11.5", + "@webassemblyjs/utf8": "1.11.5" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.5.tgz", + "integrity": "sha512-tcKwlIXstBQgbKy1MlbDMlXaxpucn42eb17H29rawYLxm5+MsEmgPzeCP8B1Cl69hCice8LeKgZpRUAPtqYPgw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5", + "@webassemblyjs/wasm-parser": "1.11.5" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.5.tgz", + "integrity": "sha512-SVXUIwsLQlc8srSD7jejsfTU83g7pIGr2YYNb9oHdtldSxaOhvA5xwvIiWIfcX8PlSakgqMXsLpLfbbJ4cBYew==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-api-error": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/ieee754": "1.11.5", + "@webassemblyjs/leb128": "1.11.5", + "@webassemblyjs/utf8": "1.11.5" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.5.tgz", + "integrity": "sha512-f7Pq3wvg3GSPUPzR0F6bmI89Hdb+u9WXrSKc4v+N0aV0q6r42WoF92Jp2jEorBEBRoRNXgjp53nBniDXcqZYPA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.5", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", + "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", + "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", + "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz", + "integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aggregate-error/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/algo-msgpack-with-bigint": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/algo-msgpack-with-bigint/-/algo-msgpack-with-bigint-2.1.1.tgz", + "integrity": "sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "dev": true, + "dependencies": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error-formatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error-formatter/-/assertion-error-formatter-2.0.1.tgz", + "integrity": "sha512-cjC3jUCh9spkroKue5PDSKH5RFQ/KNuZJhk3GwHYmB/8qqETxLOmMdLH+ohi/VukNzxDlMvIe7zScvLoOdhb6Q==", + "dev": true, + "dependencies": { + "diff": "^3.0.0", + "pad-right": "^0.2.2", + "repeat-string": "^1.6.1" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/available-typed-arrays": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz", + "integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", + "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/becke-ch--regex--s0-0-v1--base--pl--lib": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/becke-ch--regex--s0-0-v1--base--pl--lib/-/becke-ch--regex--s0-0-v1--base--pl--lib-1.4.0.tgz", + "integrity": "sha1-Qpzuu/pffpNueNc/vcfacWKyDiA=", + "dev": true + }, + "node_modules/bignumber.js": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/body-parser/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dev": true, + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001236", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001236.tgz", + "integrity": "sha512-o0PRQSrSCGJKCPZcgMzl5fUaj5xHe8qA2m4QRvnyY4e1lITqoNkr7q/Oh1NcpGSy0Th97UZ35yoKcINPoq7YOQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/chromedriver": { + "version": "108.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-108.0.0.tgz", + "integrity": "sha512-/kb0rb0dlC4RfXh2BOT7RV87K6d+It3VV5YXebLzO5a8t2knNffiTE23XPJQCH+l1xmgoW8/sOX/NB9irskvOQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@testim/chrome-version": "^1.1.3", + "axios": "^1.1.3", + "compare-versions": "^5.0.1", + "extract-zip": "^2.0.1", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^1.1.0", + "tcp-port-used": "^1.0.1" + }, + "bin": { + "chromedriver": "bin/chromedriver" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/chromedriver/node_modules/compare-versions": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz", + "integrity": "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ==", + "dev": true + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/concurrently": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.2.0.tgz", + "integrity": "sha512-v9I4Y3wFoXCSY2L73yYgwA9ESrQMpRn80jMcqMgHx720Hecz2GZAvTI6bREVST6lkddNypDKRN22qhK0X8Y00g==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "read-pkg": "^5.2.0", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "bin": { + "concurrently": "bin/concurrently.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", + "dev": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dependencies": { + "node-fetch": "2.6.7" + } + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/cucumber": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cucumber/-/cucumber-5.1.0.tgz", + "integrity": "sha512-zrl2VYTBRgvxucwV2GKAvLqcfA1Naeax8plPvWgPEzl3SCJiuPPv3WxBHIRHtPYcEdbHDR6oqLpZP4bJ8UIdmA==", + "deprecated": "Cucumber is publishing new releases under @cucumber/cucumber", + "dev": true, + "dependencies": { + "@babel/polyfill": "^7.2.3", + "assertion-error-formatter": "^2.0.1", + "bluebird": "^3.4.1", + "cli-table3": "^0.5.1", + "colors": "^1.1.2", + "commander": "^2.9.0", + "cross-spawn": "^6.0.5", + "cucumber-expressions": "^6.0.0", + "cucumber-tag-expressions": "^1.1.1", + "duration": "^0.2.1", + "escape-string-regexp": "^1.0.5", + "figures": "2.0.0", + "gherkin": "^5.0.0", + "glob": "^7.1.3", + "indent-string": "^3.1.0", + "is-generator": "^1.0.2", + "is-stream": "^1.1.0", + "knuth-shuffle-seeded": "^1.0.6", + "lodash": "^4.17.10", + "mz": "^2.4.0", + "progress": "^2.0.0", + "resolve": "^1.3.3", + "serialize-error": "^3.0.0", + "stack-chain": "^2.0.0", + "stacktrace-js": "^2.0.0", + "string-argv": "0.1.1", + "title-case": "^2.1.1", + "util-arity": "^1.0.2", + "verror": "^1.9.0" + }, + "bin": { + "cucumber-js": "bin/cucumber-js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cucumber-expressions": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/cucumber-expressions/-/cucumber-expressions-6.6.2.tgz", + "integrity": "sha512-WcFSVBiWNLJbIcAAC3t/ACU46vaOKfe1UIF5H3qveoq+Y4XQm9j3YwHurQNufRKBBg8nCnpU7Ttsx7egjS3hwA==", + "deprecated": "This package is now published under @cucumber/cucumber-expressions", + "dev": true, + "dependencies": { + "becke-ch--regex--s0-0-v1--base--pl--lib": "^1.2.0" + } + }, + "node_modules/cucumber-tag-expressions": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cucumber-tag-expressions/-/cucumber-tag-expressions-1.1.1.tgz", + "integrity": "sha1-f1x7cACbwrZmWRv+ZIVFeL7e6Fo=", + "dev": true + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/date-fns": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.22.1.tgz", + "integrity": "sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg==", + "dev": true, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/duration": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", + "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.46" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.3.751", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.751.tgz", + "integrity": "sha512-+qEPTSrwAwfHiavFmbTJ8np1NUYFmeXaDUxIj1+x1zOsYDBgWnl5Z8GeVZqis1Ljp4BlRqoZygRsez2Lg9DJgw==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz", + "integrity": "sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", + "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", + "dev": true, + "dependencies": { + "stackframe": "^1.1.1" + } + }, + "node_modules/es-abstract": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.10.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-module-lexer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", + "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", + "dev": true + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dev": true, + "dependencies": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", + "dev": true + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", + "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", + "eslint-plugin-import": "^2.22.1" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", + "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "dependencies": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-module-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", + "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "pkg-dir": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.23.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", + "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.1", + "find-up": "^2.0.0", + "has": "^1.0.3", + "is-core-module": "^2.4.0", + "minimatch": "^3.0.4", + "object.values": "^1.1.3", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.9.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-plugin-tsdoc": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.14.tgz", + "integrity": "sha512-fJ3fnZRsdIoBZgzkQjv8vAj6NeeOoFkTfgosj6mKsFjX70QV256sA/wq+y/R2+OL4L8E79VVaVWrPeZnKNe8Ng==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.13.2", + "@microsoft/tsdoc-config": "0.15.2" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/execa/node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/express/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "dev": true, + "dependencies": { + "type": "^2.0.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==", + "dev": true + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/find-versions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz", + "integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==", + "dev": true, + "dependencies": { + "semver-regex": "^3.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/geckodriver": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-3.2.0.tgz", + "integrity": "sha512-p+qR2RKlI/TQoCEYrSuTaYCLqsJNni96WmEukTyXmOmLn+3FLdgPAEwMZ0sG2Cwi9hozUzGAWyT6zLuhF6cpiQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "adm-zip": "0.5.9", + "bluebird": "3.7.2", + "got": "11.8.5", + "https-proxy-agent": "5.0.1", + "tar": "6.1.11" + }, + "bin": { + "geckodriver": "bin/geckodriver" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gherkin": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gherkin/-/gherkin-5.1.0.tgz", + "integrity": "sha1-aEu7A63STq9731RPWAM+so+zxtU=", + "deprecated": "This package is now published under @cucumber/gherkin", + "dev": true, + "bin": { + "gherkin-javascript": "bin/gherkin" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/got": { + "version": "11.8.5", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", + "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hi-base32": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", + "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==" + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/husky": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.8.tgz", + "integrity": "sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^7.0.0", + "find-versions": "^4.0.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^5.0.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" + }, + "bin": { + "husky-run": "bin/run.js", + "husky-upgrade": "lib/upgrader/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/husky" + } + }, + "node_modules/husky/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/husky/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/husky/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/husky/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/husky/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/husky/node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-generator/-/is-generator-1.0.3.tgz", + "integrity": "sha1-wUwhBX7TbjKNuANHlmxpP4hjifM=", + "dev": true + }, + "node_modules/is-generator-function": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.9.tgz", + "integrity": "sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz", + "integrity": "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.0-next.2", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true + }, + "node_modules/is2": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.7.tgz", + "integrity": "sha512-4vBQoURAXC6hnLFxD4VW7uc04XiwTTl/8ydYJxKvPwkWQrSjInkuM5VZVg6BGr1/natq69zDuvO9lGpLClJqvA==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "ip-regex": "^4.1.0", + "is-url": "^1.2.4" + }, + "engines": { + "node": ">=v0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", + "dev": true + }, + "node_modules/js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "node_modules/js-sha512": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", + "integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/keyv": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/knuth-shuffle-seeded": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz", + "integrity": "sha1-AfG2VzOqdUDuCNiwF0Fk0iCB5OE=", + "dev": true, + "dependencies": { + "seed-random": "~2.2.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "node_modules/lint-staged": { + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.4.tgz", + "integrity": "sha512-EechC3DdFic/TdOPgj/RB3FicqE6932LTHCUm0Y2fsD9KGlLB+RwJl2q1IYBIvEsKzDOgn0D4gll+YxG5RsrKg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.2.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.2.0", + "dedent": "^0.7.0", + "enquirer": "^2.3.6", + "execa": "^4.1.0", + "listr2": "^3.2.2", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/lint-staged/node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/listr2": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.10.0.tgz", + "integrity": "sha512-eP40ZHihu70sSmqFNbNy2NL1YwImmlMmPh9WO5sLmPDleurMHt3n+SwEWNu2kzKScexZnkyFtc1VI0z/TGlmpw==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^1.2.2", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.7", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + } + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/marked": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz", + "integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mock-http-server": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mock-http-server/-/mock-http-server-1.4.4.tgz", + "integrity": "sha512-QVkc/cI19VDsuDoCINgFnNl5TRiAVI4c7ReCoCbqWE5Je7RrkZIqMjWAIoTExQMCMZAiaUIJpX/0ae+yOVUuxg==", + "dev": true, + "dependencies": { + "body-parser": "^1.18.1", + "connect": "^3.4.0", + "multiparty": "^4.1.2", + "underscore": "^1.8.3" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/multiparty": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.2.tgz", + "integrity": "sha512-NtZLjlvsjcoGrzojtwQwn/Tm90aWJ6XXtPppYF4WmOk/6ncdwMMKggFY2NlRRN9yiCEIVxpOfPWahVEG2HAG8Q==", + "dev": true, + "dependencies": { + "http-errors": "~1.8.0", + "safe-buffer": "5.2.1", + "uid-safe": "2.1.5" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/multiparty/node_modules/http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multiparty/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-releases": { + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", + "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true, + "bin": { + "opencollective-postinstall": "index.js" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pad-right": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", + "integrity": "sha1-b7ySQEXSRPKiokRQMGDTv8YAl3Q=", + "dev": true, + "dependencies": { + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "node_modules/regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/schema-utils": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", + "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/seed-random": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", + "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=", + "dev": true + }, + "node_modules/selenium-webdriver": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.2.0.tgz", + "integrity": "sha512-gPPXYSz4jJBM2kANRQ9cZW6KFBzR/ptxqGLtyC75eXtdgOsWWRRRyZz5F2pqdnwNmAjrCSFMMXfisJaZeWVejg==", + "dev": true, + "dependencies": { + "jszip": "^3.6.0", + "tmp": "^0.2.1", + "ws": ">=7.4.6" + }, + "engines": { + "node": ">= 10.15.0" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "node_modules/semver-regex": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz", + "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/send/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-3.0.0.tgz", + "integrity": "sha512-+y3nkkG/go1Vdw+2f/+XUXM1DXX1XcxTl99FfiD/OEPUNw4uo0i6FKABfTAN5ZcgGtjTRZcEbxcE/jtXbEY19A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shiki": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-oniguruma": "^1.6.1", + "vscode-textmate": "5.2.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-2.0.2.tgz", + "integrity": "sha512-yIYkFOsKn+OdOirRJUPQpnZiMkF74raDVQjj5ni3SzbOiA57SabeX80R5zyMQAKpvKySA3Z4a85vFX3bvpC6KQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.2", + "source-map-js": "^0.6.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", + "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/stack-chain": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-2.0.0.tgz", + "integrity": "sha512-GGrHXePi305aW7XQweYZZwiRwR7Js3MWoK/EHzzB9ROdc75nCnjSJVi21rdAGxFl+yCx2L2qdfl5y7NO4lTyqg==", + "dev": true + }, + "node_modules/stack-generator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.5.tgz", + "integrity": "sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q==", + "dev": true, + "dependencies": { + "stackframe": "^1.1.1" + } + }, + "node_modules/stackframe": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", + "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", + "dev": true + }, + "node_modules/stacktrace-gps": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz", + "integrity": "sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg==", + "dev": true, + "dependencies": { + "source-map": "0.5.6", + "stackframe": "^1.1.1" + } + }, + "node_modules/stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "dev": true, + "dependencies": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-argv": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.1.1.tgz", + "integrity": "sha512-El1Va5ehZ0XTj3Ekw4WFidXvTmt9SrC0+eigdojgtJMVtPkF0qbBe9fyNSl9eQf+kUHnTSQxdQYzuHfZy8V+DQ==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", + "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/table/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tcp-port-used": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz", + "integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==", + "dev": true, + "dependencies": { + "debug": "4.3.1", + "is2": "^2.0.6" + } + }, + "node_modules/terser": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.1.tgz", + "integrity": "sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz", + "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.5" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/terser/node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/title-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", + "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=", + "dev": true, + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.0.3" + } + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-loader": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.3.1.tgz", + "integrity": "sha512-OkyShkcZTsTwyS3Kt7a4rsT/t2qvEVQuKCTg4LJmpj9fhFR7ukGdZwV6Qq3tRUkqcXtfGpPR7+hFKHCG/0d3Lw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "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 + } + } + }, + "node_modules/ts-node/node_modules/acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "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" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedoc": { + "version": "0.23.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.8.tgz", + "integrity": "sha512-NLRTY/7XSrhiowR3xnH/nlfTnHk+dkzhHWAMT8guoZ6RHCQZIu3pJREMCqzdkWVCC5+dr9We7TtNeprR3Qy6Ag==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.0.16", + "minimatch": "^5.1.0", + "shiki": "^0.10.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 14.14" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x" + } + }, + "node_modules/typedoc-plugin-missing-exports": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-0.23.0.tgz", + "integrity": "sha512-9smahDSsFRno9ZwoEshQDuIYMHWGB1E6LUud5qMxR2wNZ0T4DlZz0QjoK3HzXtX34mUpTH0dYtt7NQUK4D6B6Q==", + "dev": true, + "peerDependencies": { + "typedoc": "0.22.x || 0.23.x" + } + }, + "node_modules/typedoc-plugin-rename-defaults": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/typedoc-plugin-rename-defaults/-/typedoc-plugin-rename-defaults-0.6.4.tgz", + "integrity": "sha512-0rAeNttAfu6ixbi1yu6d+DqNZN8SfRivj2QbnZ4zVa+5HcCPcmQrlR6WHjNzdDfpkGytiiqPTtRD6pAfW/yACg==", + "dev": true, + "peerDependencies": { + "typedoc": "0.22.x || 0.23.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dev": true, + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/underscore": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", + "dev": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-arity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", + "integrity": "sha1-WdAa8f2z/t4KxOYysKtfbOl8kzA=", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vlq": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-2.0.4.tgz", + "integrity": "sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==" + }, + "node_modules/vscode-oniguruma": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/webpack": { + "version": "5.81.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.81.0.tgz", + "integrity": "sha512-AAjaJ9S4hYCVODKLQTgG5p5e11hiMawBwV2v8MYLE0C/6UAGLuAF4n1qa9GOwdxnicaP+5k6M5HrLmD4+gIB8Q==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.13.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", + "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.0.1", + "@webpack-cli/info": "^2.0.1", + "@webpack-cli/serve": "^2.0.1", + "colorette": "^2.0.14", + "commander": "^9.4.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/webpack-cli/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/webpack-cli/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-cli/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-cli/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-cli/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack/node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, + "node_modules/which-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/polyfill": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.12.1.tgz", + "integrity": "sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==", + "dev": true, + "requires": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.4" + } + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@microsoft/tsdoc": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz", + "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==", + "dev": true + }, + "@microsoft/tsdoc-config": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.15.2.tgz", + "integrity": "sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.13.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + }, + "dependencies": { + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", + "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@testim/chrome-version": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.3.tgz", + "integrity": "sha512-g697J3WxV/Zytemz8aTuKjTGYtta9+02kva3C1xc7KXB8GdbfE1akGJIsZLyY/FSh2QrnE+fiB7vmWU3XNcb6A==", + "dev": true + }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.8.tgz", + "integrity": "sha512-LM6XwBhjZRls1qJGpiM/It09SntEwe9M0riXRfQ9s6XlJQG0JPGl92ET18LtGeYh/GuOtafIXqwZeqLOd0FNFQ==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "@types/eslint": { + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", + "dev": true + }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "dev": true + }, + "@types/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-WW+0cfH3ovFN6ROV+p/Xfw36dT6s16hbXBYIG49PYw6+j6e+AkpqYccctgxwyicBmC8CZDBnPhOH94shFhXgHQ==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/mocha": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", + "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", + "dev": true + }, + "@types/node": { + "version": "15.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", + "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.1.tgz", + "integrity": "sha512-aoIusj/8CR+xDWmZxARivZjbMBQTT9dImUtdZ8tVCVRXgBUuuZyM5Of5A9D9arQPxbi/0rlJLcuArclz/rCMJw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.26.1", + "@typescript-eslint/scope-manager": "4.26.1", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "lodash": "^4.17.21", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.1.tgz", + "integrity": "sha512-sQHBugRhrXzRCs9PaGg6rowie4i8s/iD/DpTB+EXte8OMDfdCG5TvO73XlO9Wc/zi0uyN4qOmX9hIjQEyhnbmQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.26.1", + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/typescript-estree": "4.26.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.26.1.tgz", + "integrity": "sha512-q7F3zSo/nU6YJpPJvQveVlIIzx9/wu75lr6oDbDzoeIRWxpoc/HQ43G4rmMoCc5my/3uSj2VEpg/D83LYZF5HQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.26.1", + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/typescript-estree": "4.26.1", + "debug": "^4.3.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.26.1.tgz", + "integrity": "sha512-TW1X2p62FQ8Rlne+WEShyd7ac2LA6o27S9i131W4NwDSfyeVlQWhw8ylldNNS8JG6oJB9Ha9Xyc+IUcqipvheQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/visitor-keys": "4.26.1" + } + }, + "@typescript-eslint/types": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.26.1.tgz", + "integrity": "sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz", + "integrity": "sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/visitor-keys": "4.26.1", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.1.tgz", + "integrity": "sha512-IGouNSSd+6x/fHtYRyLOM6/C+QxMDzWlDtN41ea+flWuSF9g02iqcIlX8wM53JkfljoIjP0U+yp7SiTS1onEkw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.26.1", + "eslint-visitor-keys": "^2.0.0" + } + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.5.tgz", + "integrity": "sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.5.tgz", + "integrity": "sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.5.tgz", + "integrity": "sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.5.tgz", + "integrity": "sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.5.tgz", + "integrity": "sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.5", + "@webassemblyjs/helper-api-error": "1.11.5", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.5.tgz", + "integrity": "sha512-oC4Qa0bNcqnjAowFn7MPCETQgDYytpsfvz4ujZz63Zu/a/v71HeCAAmZsgZ3YVKec3zSPYytG3/PrRCqbtcAvA==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.5.tgz", + "integrity": "sha512-uEoThA1LN2NA+K3B9wDo3yKlBfVtC6rh0i4/6hvbz071E8gTNZD/pT0MsBf7MeD6KbApMSkaAK0XeKyOZC7CIA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.5.tgz", + "integrity": "sha512-37aGq6qVL8A8oPbPrSGMBcp38YZFXcHfiROflJn9jxSdSMMM5dS5P/9e2/TpaJuhE+wFrbukN2WI6Hw9MH5acg==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.5.tgz", + "integrity": "sha512-ajqrRSXaTJoPW+xmkfYN6l8VIeNnR4vBOTQO9HzR7IygoCcKWkICbKFbVTNMjMgMREqXEr0+2M6zukzM47ZUfQ==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.5.tgz", + "integrity": "sha512-WiOhulHKTZU5UPlRl53gHR8OxdGsSOxqfpqWeA2FmcwBMaoEdz6b2x2si3IwC9/fSPLfe8pBMRTHVMk5nlwnFQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.5.tgz", + "integrity": "sha512-C0p9D2fAu3Twwqvygvf42iGCQ4av8MFBLiTb+08SZ4cEdwzWx9QeAHDo1E2k+9s/0w1DM40oflJOpkZ8jW4HCQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/helper-wasm-section": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5", + "@webassemblyjs/wasm-opt": "1.11.5", + "@webassemblyjs/wasm-parser": "1.11.5", + "@webassemblyjs/wast-printer": "1.11.5" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.5.tgz", + "integrity": "sha512-14vteRlRjxLK9eSyYFvw1K8Vv+iPdZU0Aebk3j6oB8TQiQYuO6hj9s4d7qf6f2HJr2khzvNldAFG13CgdkAIfA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/ieee754": "1.11.5", + "@webassemblyjs/leb128": "1.11.5", + "@webassemblyjs/utf8": "1.11.5" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.5.tgz", + "integrity": "sha512-tcKwlIXstBQgbKy1MlbDMlXaxpucn42eb17H29rawYLxm5+MsEmgPzeCP8B1Cl69hCice8LeKgZpRUAPtqYPgw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5", + "@webassemblyjs/wasm-parser": "1.11.5" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.5.tgz", + "integrity": "sha512-SVXUIwsLQlc8srSD7jejsfTU83g7pIGr2YYNb9oHdtldSxaOhvA5xwvIiWIfcX8PlSakgqMXsLpLfbbJ4cBYew==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-api-error": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/ieee754": "1.11.5", + "@webassemblyjs/leb128": "1.11.5", + "@webassemblyjs/utf8": "1.11.5" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.5.tgz", + "integrity": "sha512-f7Pq3wvg3GSPUPzR0F6bmI89Hdb+u9WXrSKc4v+N0aV0q6r42WoF92Jp2jEorBEBRoRNXgjp53nBniDXcqZYPA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.5", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", + "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", + "dev": true, + "requires": {} + }, + "@webpack-cli/info": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", + "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", + "dev": true, + "requires": {} + }, + "@webpack-cli/serve": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", + "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", + "dev": true, + "requires": {} + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "adm-zip": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz", + "integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg==", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + } + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "algo-msgpack-with-bigint": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/algo-msgpack-with-bigint/-/algo-msgpack-with-bigint-2.1.1.tgz", + "integrity": "sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ==" + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.5" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "dev": true, + "requires": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assertion-error-formatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error-formatter/-/assertion-error-formatter-2.0.1.tgz", + "integrity": "sha512-cjC3jUCh9spkroKue5PDSKH5RFQ/KNuZJhk3GwHYmB/8qqETxLOmMdLH+ohi/VukNzxDlMvIe7zScvLoOdhb6Q==", + "dev": true, + "requires": { + "diff": "^3.0.0", + "pad-right": "^0.2.2", + "repeat-string": "^1.6.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz", + "integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==", + "dev": true + }, + "axios": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", + "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "dev": true, + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "becke-ch--regex--s0-0-v1--base--pl--lib": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/becke-ch--regex--s0-0-v1--base--pl--lib/-/becke-ch--regex--s0-0-v1--base--pl--lib-1.4.0.tgz", + "integrity": "sha1-Qpzuu/pffpNueNc/vcfacWKyDiA=", + "dev": true + }, + "bignumber.js": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true + }, + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001236", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001236.tgz", + "integrity": "sha512-o0PRQSrSCGJKCPZcgMzl5fUaj5xHe8qA2m4QRvnyY4e1lITqoNkr7q/Oh1NcpGSy0Th97UZ35yoKcINPoq7YOQ==", + "dev": true + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "chromedriver": { + "version": "108.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-108.0.0.tgz", + "integrity": "sha512-/kb0rb0dlC4RfXh2BOT7RV87K6d+It3VV5YXebLzO5a8t2knNffiTE23XPJQCH+l1xmgoW8/sOX/NB9irskvOQ==", + "dev": true, + "requires": { + "@testim/chrome-version": "^1.1.3", + "axios": "^1.1.3", + "compare-versions": "^5.0.1", + "extract-zip": "^2.0.1", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^1.1.0", + "tcp-port-used": "^1.0.1" + }, + "dependencies": { + "compare-versions": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz", + "integrity": "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ==", + "dev": true + } + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + } + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concurrently": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.2.0.tgz", + "integrity": "sha512-v9I4Y3wFoXCSY2L73yYgwA9ESrQMpRn80jMcqMgHx720Hecz2GZAvTI6bREVST6lkddNypDKRN22qhK0X8Y00g==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "read-pkg": "^5.2.0", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + } + }, + "confusing-browser-globals": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", + "dev": true + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "requires": { + "node-fetch": "2.6.7" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "cucumber": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cucumber/-/cucumber-5.1.0.tgz", + "integrity": "sha512-zrl2VYTBRgvxucwV2GKAvLqcfA1Naeax8plPvWgPEzl3SCJiuPPv3WxBHIRHtPYcEdbHDR6oqLpZP4bJ8UIdmA==", + "dev": true, + "requires": { + "@babel/polyfill": "^7.2.3", + "assertion-error-formatter": "^2.0.1", + "bluebird": "^3.4.1", + "cli-table3": "^0.5.1", + "colors": "^1.1.2", + "commander": "^2.9.0", + "cross-spawn": "^6.0.5", + "cucumber-expressions": "^6.0.0", + "cucumber-tag-expressions": "^1.1.1", + "duration": "^0.2.1", + "escape-string-regexp": "^1.0.5", + "figures": "2.0.0", + "gherkin": "^5.0.0", + "glob": "^7.1.3", + "indent-string": "^3.1.0", + "is-generator": "^1.0.2", + "is-stream": "^1.1.0", + "knuth-shuffle-seeded": "^1.0.6", + "lodash": "^4.17.10", + "mz": "^2.4.0", + "progress": "^2.0.0", + "resolve": "^1.3.3", + "serialize-error": "^3.0.0", + "stack-chain": "^2.0.0", + "stacktrace-js": "^2.0.0", + "string-argv": "0.1.1", + "title-case": "^2.1.1", + "util-arity": "^1.0.2", + "verror": "^1.9.0" + } + }, + "cucumber-expressions": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/cucumber-expressions/-/cucumber-expressions-6.6.2.tgz", + "integrity": "sha512-WcFSVBiWNLJbIcAAC3t/ACU46vaOKfe1UIF5H3qveoq+Y4XQm9j3YwHurQNufRKBBg8nCnpU7Ttsx7egjS3hwA==", + "dev": true, + "requires": { + "becke-ch--regex--s0-0-v1--base--pl--lib": "^1.2.0" + } + }, + "cucumber-tag-expressions": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cucumber-tag-expressions/-/cucumber-tag-expressions-1.1.1.tgz", + "integrity": "sha1-f1x7cACbwrZmWRv+ZIVFeL7e6Fo=", + "dev": true + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "date-fns": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.22.1.tgz", + "integrity": "sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg==", + "dev": true + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "duration": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", + "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.46" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.751", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.751.tgz", + "integrity": "sha512-+qEPTSrwAwfHiavFmbTJ8np1NUYFmeXaDUxIj1+x1zOsYDBgWnl5Z8GeVZqis1Ljp4BlRqoZygRsez2Lg9DJgw==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz", + "integrity": "sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", + "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", + "dev": true, + "requires": { + "stackframe": "^1.1.1" + } + }, + "es-abstract": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.10.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-module-lexer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", + "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", + "dev": true + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", + "dev": true + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", + "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "eslint-config-airbnb-base": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + } + }, + "eslint-config-prettier": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", + "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "dev": true, + "requires": {} + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", + "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.23.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", + "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.1", + "find-up": "^2.0.0", + "has": "^1.0.3", + "is-core-module": "^2.4.0", + "minimatch": "^3.0.4", + "object.values": "^1.1.3", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-tsdoc": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.14.tgz", + "integrity": "sha512-fJ3fnZRsdIoBZgzkQjv8vAj6NeeOoFkTfgosj6mKsFjX70QV256sA/wq+y/R2+OL4L8E79VVaVWrPeZnKNe8Ng==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.13.2", + "@microsoft/tsdoc-config": "0.15.2" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "dev": true, + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==", + "dev": true + } + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true + }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "find-versions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz", + "integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==", + "dev": true, + "requires": { + "semver-regex": "^3.1.2" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "geckodriver": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-3.2.0.tgz", + "integrity": "sha512-p+qR2RKlI/TQoCEYrSuTaYCLqsJNni96WmEukTyXmOmLn+3FLdgPAEwMZ0sG2Cwi9hozUzGAWyT6zLuhF6cpiQ==", + "dev": true, + "requires": { + "adm-zip": "0.5.9", + "bluebird": "3.7.2", + "got": "11.8.5", + "https-proxy-agent": "5.0.1", + "tar": "6.1.11" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "gherkin": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gherkin/-/gherkin-5.1.0.tgz", + "integrity": "sha1-aEu7A63STq9731RPWAM+so+zxtU=", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "globals": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } + } + }, + "got": { + "version": "11.8.5", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", + "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==", + "dev": true, + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hi-base32": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", + "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==" + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + } + } + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "husky": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.8.tgz", + "integrity": "sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^7.0.0", + "find-versions": "^4.0.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^5.0.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "requires": { + "find-up": "^5.0.0" + } + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true + }, + "ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "dev": true + }, + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-generator": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-generator/-/is-generator-1.0.3.tgz", + "integrity": "sha1-wUwhBX7TbjKNuANHlmxpP4hjifM=", + "dev": true + }, + "is-generator-function": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.9.tgz", + "integrity": "sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "dev": true + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", + "dev": true + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz", + "integrity": "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.0-next.2", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + } + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true + }, + "is2": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.7.tgz", + "integrity": "sha512-4vBQoURAXC6hnLFxD4VW7uc04XiwTTl/8ydYJxKvPwkWQrSjInkuM5VZVg6BGr1/natq69zDuvO9lGpLClJqvA==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "ip-regex": "^4.1.0", + "is-url": "^1.2.4" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", + "dev": true + }, + "js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "js-sha512": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", + "integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "keyv": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "knuth-shuffle-seeded": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz", + "integrity": "sha1-AfG2VzOqdUDuCNiwF0Fk0iCB5OE=", + "dev": true, + "requires": { + "seed-random": "~2.2.0" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "lint-staged": { + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.4.tgz", + "integrity": "sha512-EechC3DdFic/TdOPgj/RB3FicqE6932LTHCUm0Y2fsD9KGlLB+RwJl2q1IYBIvEsKzDOgn0D4gll+YxG5RsrKg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.2.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.2.0", + "dedent": "^0.7.0", + "enquirer": "^2.3.6", + "execa": "^4.1.0", + "listr2": "^3.2.2", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + }, + "dependencies": { + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + } + } + }, + "listr2": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.10.0.tgz", + "integrity": "sha512-eP40ZHihu70sSmqFNbNy2NL1YwImmlMmPh9WO5sLmPDleurMHt3n+SwEWNu2kzKScexZnkyFtc1VI0z/TGlmpw==", + "dev": true, + "requires": { + "cli-truncate": "^2.1.0", + "colorette": "^1.2.2", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.7", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } + } + }, + "loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "marked": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz", + "integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "mock-http-server": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mock-http-server/-/mock-http-server-1.4.4.tgz", + "integrity": "sha512-QVkc/cI19VDsuDoCINgFnNl5TRiAVI4c7ReCoCbqWE5Je7RrkZIqMjWAIoTExQMCMZAiaUIJpX/0ae+yOVUuxg==", + "dev": true, + "requires": { + "body-parser": "^1.18.1", + "connect": "^3.4.0", + "multiparty": "^4.1.2", + "underscore": "^1.8.3" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "multiparty": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.2.tgz", + "integrity": "sha512-NtZLjlvsjcoGrzojtwQwn/Tm90aWJ6XXtPppYF4WmOk/6ncdwMMKggFY2NlRRN9yiCEIVxpOfPWahVEG2HAG8Q==", + "dev": true, + "requires": { + "http-errors": "~1.8.0", + "safe-buffer": "5.2.1", + "uid-safe": "2.1.5" + }, + "dependencies": { + "http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-releases": { + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + }, + "dependencies": { + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "dev": true + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", + "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + } + }, + "object.values": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pad-right": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", + "integrity": "sha1-b7ySQEXSRPKiokRQMGDTv8YAl3Q=", + "dev": true, + "requires": { + "repeat-string": "^1.5.2" + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "dependencies": { + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "requires": { + "resolve": "^1.20.0" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", + "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "seed-random": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", + "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=", + "dev": true + }, + "selenium-webdriver": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.2.0.tgz", + "integrity": "sha512-gPPXYSz4jJBM2kANRQ9cZW6KFBzR/ptxqGLtyC75eXtdgOsWWRRRyZz5F2pqdnwNmAjrCSFMMXfisJaZeWVejg==", + "dev": true, + "requires": { + "jszip": "^3.6.0", + "tmp": "^0.2.1", + "ws": ">=7.4.6" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-regex": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz", + "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==", + "dev": true + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "serialize-error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-3.0.0.tgz", + "integrity": "sha512-+y3nkkG/go1Vdw+2f/+XUXM1DXX1XcxTl99FfiD/OEPUNw4uo0i6FKABfTAN5ZcgGtjTRZcEbxcE/jtXbEY19A==", + "dev": true + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shiki": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "dev": true, + "requires": { + "jsonc-parser": "^3.0.0", + "vscode-oniguruma": "^1.6.1", + "vscode-textmate": "5.2.0" + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true + }, + "source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true + }, + "source-map-loader": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-2.0.2.tgz", + "integrity": "sha512-yIYkFOsKn+OdOirRJUPQpnZiMkF74raDVQjj5ni3SzbOiA57SabeX80R5zyMQAKpvKySA3Z4a85vFX3bvpC6KQ==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.2", + "source-map-js": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", + "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "stack-chain": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-2.0.0.tgz", + "integrity": "sha512-GGrHXePi305aW7XQweYZZwiRwR7Js3MWoK/EHzzB9ROdc75nCnjSJVi21rdAGxFl+yCx2L2qdfl5y7NO4lTyqg==", + "dev": true + }, + "stack-generator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.5.tgz", + "integrity": "sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q==", + "dev": true, + "requires": { + "stackframe": "^1.1.1" + } + }, + "stackframe": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", + "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", + "dev": true + }, + "stacktrace-gps": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz", + "integrity": "sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg==", + "dev": true, + "requires": { + "source-map": "0.5.6", + "stackframe": "^1.1.1" + } + }, + "stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "dev": true, + "requires": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-argv": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.1.1.tgz", + "integrity": "sha512-El1Va5ehZ0XTj3Ekw4WFidXvTmt9SrC0+eigdojgtJMVtPkF0qbBe9fyNSl9eQf+kUHnTSQxdQYzuHfZy8V+DQ==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", + "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "tcp-port-used": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz", + "integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==", + "dev": true, + "requires": { + "debug": "4.3.1", + "is2": "^2.0.6" + } + }, + "terser": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.1.tgz", + "integrity": "sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz", + "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.5" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "title-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", + "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.0.3" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "ts-loader": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.3.1.tgz", + "integrity": "sha512-OkyShkcZTsTwyS3Kt7a4rsT/t2qvEVQuKCTg4LJmpj9fhFR7ukGdZwV6Qq3tRUkqcXtfGpPR7+hFKHCG/0d3Lw==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + } + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedoc": { + "version": "0.23.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.8.tgz", + "integrity": "sha512-NLRTY/7XSrhiowR3xnH/nlfTnHk+dkzhHWAMT8guoZ6RHCQZIu3pJREMCqzdkWVCC5+dr9We7TtNeprR3Qy6Ag==", + "dev": true, + "requires": { + "lunr": "^2.3.9", + "marked": "^4.0.16", + "minimatch": "^5.1.0", + "shiki": "^0.10.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "typedoc-plugin-missing-exports": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-0.23.0.tgz", + "integrity": "sha512-9smahDSsFRno9ZwoEshQDuIYMHWGB1E6LUud5qMxR2wNZ0T4DlZz0QjoK3HzXtX34mUpTH0dYtt7NQUK4D6B6Q==", + "dev": true, + "requires": {} + }, + "typedoc-plugin-rename-defaults": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/typedoc-plugin-rename-defaults/-/typedoc-plugin-rename-defaults-0.6.4.tgz", + "integrity": "sha512-0rAeNttAfu6ixbi1yu6d+DqNZN8SfRivj2QbnZ4zVa+5HcCPcmQrlR6WHjNzdDfpkGytiiqPTtRD6pAfW/yACg==", + "dev": true, + "requires": {} + }, + "typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true + }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dev": true, + "requires": { + "random-bytes": "~1.0.0" + } + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "underscore": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "util-arity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", + "integrity": "sha1-WdAa8f2z/t4KxOYysKtfbOl8kzA=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vlq": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-2.0.4.tgz", + "integrity": "sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==" + }, + "vscode-oniguruma": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", + "dev": true + }, + "vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "webpack": { + "version": "5.81.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.81.0.tgz", + "integrity": "sha512-AAjaJ9S4hYCVODKLQTgG5p5e11hiMawBwV2v8MYLE0C/6UAGLuAF4n1qa9GOwdxnicaP+5k6M5HrLmD4+gIB8Q==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.13.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "dev": true + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "requires": {} + } + } + }, + "webpack-cli": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", + "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.0.1", + "@webpack-cli/info": "^2.0.1", + "@webpack-cli/serve": "^2.0.1", + "colorette": "^2.0.14", + "commander": "^9.4.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "commander": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, + "which-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true, + "requires": {} + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "dependencies": { + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7cd929c --- /dev/null +++ b/package.json @@ -0,0 +1,85 @@ +{ + "name": "algosdk", + "version": "7.7.5", + "description": "The official JavaScript SDK for Algorand", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "browser": { + ".": "dist/browser/algosdk.min.js", + "crypto": false + }, + "types": "dist/types/index.d.ts", + "files": [ + "dist/", + "src/" + ], + "directories": { + "test": "tests" + }, + "repository": { + "type": "git", + "url": "git://github.com/algorand/js-algorand-sdk.git" + }, + "dependencies": { + "algo-msgpack-with-bigint": "^2.1.1", + "buffer": "^6.0.3", + "cross-fetch": "^3.1.5", + "hi-base32": "^0.5.1", + "js-sha256": "^0.9.0", + "js-sha3": "^0.8.0", + "js-sha512": "^0.8.0", + "json-bigint": "^1.0.0", + "tweetnacl": "^1.0.3", + "vlq": "^2.0.4" + }, + "devDependencies": { + "@types/json-bigint": "^1.0.0", + "@types/mocha": "^8.2.2", + "@typescript-eslint/eslint-plugin": "^4.26.1", + "@typescript-eslint/parser": "^4.26.1", + "assert": "^2.0.0", + "chromedriver": "^108.0.0", + "concurrently": "^6.2.0", + "cucumber": "^5.1.0", + "es-abstract": "^1.18.3", + "eslint": "^7.21.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-config-prettier": "^8.1.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-tsdoc": "^0.2.11", + "express": "^4.17.1", + "geckodriver": "^3.0.1", + "husky": "^4.3.8", + "lint-staged": "^10.5.4", + "mocha": "^9.0.0", + "mock-http-server": "^1.4.3", + "prettier": "2.2.1", + "selenium-webdriver": "^4.2.0", + "source-map-loader": "^2.0.2", + "ts-loader": "^9.3.1", + "ts-node": "^10.9.1", + "typedoc": "^0.23.8", + "typedoc-plugin-missing-exports": "^0.23.0", + "typedoc-plugin-rename-defaults": "^0.6.4", + "typescript": "^4.7.4", + "webpack": "^5.75.0", + "webpack-cli": "^5.0.1" + }, + "scripts": { + "test": "node -r ts-node/register tests/mocha.js", + "prepare": "npm run build", + "prepare-browser-tests": "npm run build && mkdir -p tests/cucumber/browser/build && cp dist/browser/algosdk.min.* tests/cucumber/browser/build/ && webpack --config tests/cucumber/browser/webpack.config.js", + "build": "concurrently \"webpack --config webpack.config.js\" \"tsc -p tsconfig-esm.json\" \"tsc -p tsconfig-cjs.json\"", + "docs": "typedoc --options typedoc.config.json", + "docs:dev": "typedoc --options typedoc.config.json --watch --preserveWatchOutput", + "lint": "eslint .", + "lint:fix": "eslint --fix .", + "format": "prettier --write .", + "example": "ts-node" + }, + "author": "Algorand, llc", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } +} diff --git a/pom.xml b/pom.xml new file mode 100755 index 0000000..ec8d71f --- /dev/null +++ b/pom.xml @@ -0,0 +1,493 @@ + + + 4.0.0 + + com.algorand + algosdk + 2.1.0 + jar + + ${project.groupId}:${project.artifactId} + Client library for Algorand + https://github.com/algorand/java-algorand-sdk + + + scm:git:git@github.com:algorand/java-algorand-sdk.git + scm:git:git@github.com:algorand/java-algorand-sdk.git + https://github.com/algorand/java-algorand-sdk + + + + + MIT License + https://github.com/algorand/java-algorand-sdk/blob/master/LICENSE + repo + + + + + + Benjamin Chan + ben.chan@algorand.com + + + + + 5.6.2 + 3.17.1 + 6.6.0 + 3.5.7 + 3.8.1 + 3.2.0 + 3.0.0-M5 + + + 1.7 + 7 + 8 + + + github + UTF-8 + + + + + com.fasterxml.jackson.core + jackson-annotations + 2.10.0 + + + com.fasterxml.jackson.core + jackson-core + 2.10.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.10.5.1 + + + com.google.code.gson + gson + 2.8.1 + + + com.squareup.okhttp + logging-interceptor + 2.7.5 + + + com.squareup.okhttp + okhttp + 2.7.5 + + + com.squareup.okio + okio + 1.6.0 + + + commons-codec + commons-codec + + 1.12 + + + io.gsonfire + gson-fire + 1.8.0 + + + io.swagger + swagger-annotations + 1.5.18 + + + org.apache.commons + commons-lang3 + + 3.8 + + + org.bouncycastle + bcprov-jdk15to18 + 1.66 + + + org.msgpack + jackson-dataformat-msgpack + 0.9.0 + + + org.threeten + threetenbp + 1.3.5 + + + com.google.guava + guava + 28.2-android + + + + + + + org.assertj + assertj-core + ${assertj.version} + test + + + + + org.junit.jupiter + junit-jupiter + ${junit5.version} + test + + + + org.junit.jupiter + junit-jupiter-api + ${junit5.version} + test + + + + org.junit.vintage + junit-vintage-engine + ${junit5.version} + test + + + + org.junit.jupiter + junit-jupiter-params + ${junit5.version} + test + + + + org.mockito + mockito-core + ${mockito.version} + test + + + + + io.cucumber + cucumber-java + ${cucumber.version} + test + + + + io.cucumber + cucumber-junit + ${cucumber.version} + test + + + + + io.cucumber + cucumber-picocontainer + ${cucumber.version} + test + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.version} + + true + + + + unit-tests + test + + test + + + false + + **/*.* + + + + + + + + + + + default + + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + ${java.release.version} + ${java.test.release.version} + + + + + + + + + + + ide + + false + + idea.maven.embedder.version + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + ${java.test.release.version} + ${java.test.release.version} + + + + + + + + + release + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven.javadoc.plugin.version} + + ${java.version} + none + + + + attach-javadocs + + jar + + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + --pinentry-mode + loopback + + + + + + + + + github + + + internal.repo + Temporary Staging Repository + file://${project.build.directory}/mvn-repo + + + + + + maven-deploy-plugin + 2.8.1 + + internal.repo::default::file://${project.build.directory}/mvn-repo + + + + com.github.github + site-maven-plugin + 0.11 + + Maven artifacts for ${project.version} + true + ${project.build.directory}/mvn-repo + refs/heads/mvn-repo + **/* + java-algorand-sdk + algorand + + + + + + site + + deploy + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven.javadoc.plugin.version} + + ${java.version} + none + + + + gen-javadocs + + javadoc + + site + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.0.0 + + + org.apache.maven.plugins + maven-site-plugin + 3.7.1 + + + org.apache.maven.plugins + maven-scm-publish-plugin + 3.0.0 + + ${project.build.directory}/scmpublish + Publishing javadoc for ${project.artifactId}:${project.version} + ${project.reporting.outputDirectory}/apidocs + true + scm:git:git@github.com:algorand/java-algorand-sdk.git + gh-pages + + + + + publish-scm + + site + + + + + + + + diff --git a/scripts/bump_version.py b/scripts/bump_version.py index cd388da..c8f107e 100755 --- a/scripts/bump_version.py +++ b/scripts/bump_version.py @@ -1,34 +1,78 @@ -# This script bumps up the version in setup.py for new releases. -# Usage: python bump_version.py {new_version} (--setup_py_path ) +# This script bumps up the version in `pom.xml` and `README.md` for new releases. +# Usage: python bump_version.py {new_version} (--read_me --pom_xml ) import argparse import re +import json +import sys +def check_version(new_version): + if not re.fullmatch(r"[0-9]+\.[0-9]+\.[-a-z.0-9]+", new_version): + sys.exit("The version does not match the regex(major.minor.patch): [0-9]+\.[0-9]+\.[-a-z.0-9]+") -def bump_version(new_version, setup_py_path): - with open(setup_py_path, "r") as file: - setup_py = file.read() +def bump_package_json(new_version, file_path): + with open(file_path, "r") as file: + content = json.load(file) - new_setup_py = re.sub( - 'version="[0-9]+\.[0-9]+\.[a-z.0-9]+"', - f'version="{new_version}"', - setup_py, - ) + content["version"] = new_version + + with open(file_path, "w") as file: + json.dump(content, file, indent=2) + +def bump_package_lock_json(new_version, file_path): + with open(file_path, "r") as file: + content = json.load(file) + + content["version"] = new_version + content["packages"][""]["version"] = new_version - with open(setup_py_path, "w") as file: - file.write(new_setup_py) + with open(file_path, "w") as file: + json.dump(content, file, indent=2) + +def update_read_me(new_version, new_hash, file_path): + with open(file_path, "r") as file: + content = file.read() + + # Replace version + new_content = re.sub( + 'algosdk@v[0-9]+\.[0-9]+\.[-a-z.0-9]+', + f'algosdk@v{new_version}', + content, + ) + # Replace hash + new_content = re.sub( + 'integrity="sha384-.*?"', + f'integrity="sha384-{new_hash}"', + new_content + ) + with open(file_path, "w") as file: + file.write(new_content) if __name__ == "__main__": parser = argparse.ArgumentParser( description="updates the version for a release", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) - parser.add_argument("new_version", help="New Version as major.minor.patch") + parser.add_argument("new_version", help="new version as major.minor.patch") parser.add_argument( - "-s", "--setup_py_path", default="setup.py", help="path to setup.py" + "--new_hash", required=True, help = "new integrity hash for the build" + ) + parser.add_argument( + "--package_json", default="package.json", help="path to package.json" + ) + parser.add_argument( + "--package_lock_json", default="package-lock.json", help="path to package-lock.json" + ) + parser.add_argument( + "--read_me", default="README.md", help="path to readme" ) args = parser.parse_args() + check_version(args.new_version) + bump_package_json(args.new_version, args.package_json) + bump_package_lock_json(args.new_version, args.package_lock_json) + update_read_me(args.new_version, args.new_hash, args.read_me) + + - bump_version(args.new_version, args.setup_py_path) diff --git a/src/abi/abi_type.ts b/src/abi/abi_type.ts new file mode 100644 index 0000000..cbcbd9b --- /dev/null +++ b/src/abi/abi_type.ts @@ -0,0 +1,847 @@ +/* eslint-disable no-bitwise */ +/* eslint-disable no-use-before-define */ +/* eslint-disable class-methods-use-this */ + +/** + //ABI-Types: uint: An N-bit unsigned integer (8 <= N <= 512 and N % 8 = 0). + // | byte (alias for uint8) + // | ufixed x (8 <= N <= 512, N % 8 = 0, and 0 < M <= 160) + // | bool + // | address (alias for byte[32]) + // | [] + // | [] + // | string + // | (T1, ..., Tn) +*/ +import { Buffer } from 'buffer'; +import { encodeAddress, decodeAddress } from '../encoding/address'; +import { bigIntToBytes, bytesToBigInt } from '../encoding/bigint'; +import { concatArrays } from '../utils/utils'; + +export const MAX_LEN = 2 ** 16 - 1; +export const ADDR_BYTE_SIZE = 32; +export const SINGLE_BYTE_SIZE = 1; +export const SINGLE_BOOL_SIZE = 1; +export const LENGTH_ENCODE_BYTE_SIZE = 2; + +interface Segment { + left: number; + right: number; +} + +const staticArrayRegexp = /^([a-z\d[\](),]+)\[(0|[1-9][\d]*)]$/; +const ufixedRegexp = /^ufixed([1-9][\d]*)x([1-9][\d]*)$/; + +export type ABIValue = + | boolean + | number + | bigint + | string + | Uint8Array + | ABIValue[]; + +export abstract class ABIType { + // Converts a ABIType object to a string + abstract toString(): string; + // Checks if two ABIType objects are equal in value + abstract equals(other: ABIType): boolean; + // Checks if the ABIType object (or any of its child types) have dynamic length + abstract isDynamic(): boolean; + // Returns the size of the ABIType object in bytes + abstract byteLen(): number; + // Encodes a value for the ABIType object using the ABI specs + abstract encode(value: ABIValue): Uint8Array; + // Decodes a value for the ABIType object using the ABI specs + abstract decode(byteString: Uint8Array): ABIValue; + // De-serializes the ABI type from a string using the ABI specs + static from(str: string): ABIType { + if (str.endsWith('[]')) { + const arrayArgType = ABIType.from(str.slice(0, str.length - 2)); + return new ABIArrayDynamicType(arrayArgType); + } + if (str.endsWith(']')) { + const stringMatches = str.match(staticArrayRegexp); + // Match the string itself, array element type, then array length + if (stringMatches.length !== 3) { + throw new Error(`malformed static array string: ${str}`); + } + // Parse static array using regex + const arrayLengthStr = stringMatches[2]; + const arrayLength = parseInt(arrayLengthStr, 10); + if (arrayLength > MAX_LEN) { + throw new Error(`array length exceeds limit ${MAX_LEN}`); + } + // Parse the array element type + const arrayType = ABIType.from(stringMatches[1]); + return new ABIArrayStaticType(arrayType, arrayLength); + } + if (str.startsWith('uint')) { + // Checks if the parsed number contains only digits, no whitespaces + const digitsOnly = (string) => + [...string].every((c) => '0123456789'.includes(c)); + const typeSizeStr = str.slice(4, str.length); + if (!digitsOnly(typeSizeStr)) { + throw new Error(`malformed uint string: ${typeSizeStr}`); + } + const typeSize = parseInt(typeSizeStr, 10); + if (typeSize > MAX_LEN) { + throw new Error(`malformed uint string: ${typeSize}`); + } + return new ABIUintType(typeSize); + } + if (str === 'byte') { + return new ABIByteType(); + } + if (str.startsWith('ufixed')) { + const stringMatches = str.match(ufixedRegexp); + if (stringMatches.length !== 3) { + throw new Error(`malformed ufixed type: ${str}`); + } + const ufixedSize = parseInt(stringMatches[1], 10); + const ufixedPrecision = parseInt(stringMatches[2], 10); + return new ABIUfixedType(ufixedSize, ufixedPrecision); + } + if (str === 'bool') { + return new ABIBoolType(); + } + if (str === 'address') { + return new ABIAddressType(); + } + if (str === 'string') { + return new ABIStringType(); + } + if (str.length >= 2 && str[0] === '(' && str[str.length - 1] === ')') { + const tupleContent = ABITupleType.parseTupleContent( + str.slice(1, str.length - 1) + ); + const tupleTypes: ABIType[] = []; + for (let i = 0; i < tupleContent.length; i++) { + const ti = ABIType.from(tupleContent[i]); + tupleTypes.push(ti); + } + return new ABITupleType(tupleTypes); + } + throw new Error(`cannot convert a string ${str} to an ABI type`); + } +} + +export class ABIUintType extends ABIType { + bitSize: number; + + constructor(size: number) { + super(); + if (size % 8 !== 0 || size < 8 || size > 512) { + throw new Error(`unsupported uint type bitSize: ${size}`); + } + this.bitSize = size; + } + + toString() { + return `uint${this.bitSize}`; + } + + equals(other: ABIType) { + return other instanceof ABIUintType && this.bitSize === other.bitSize; + } + + isDynamic() { + return false; + } + + byteLen() { + return this.bitSize / 8; + } + + encode(value: ABIValue) { + if (typeof value !== 'bigint' && typeof value !== 'number') { + throw new Error(`Cannot encode value as uint${this.bitSize}: ${value}`); + } + if (value >= BigInt(2 ** this.bitSize) || value < BigInt(0)) { + throw new Error( + `${value} is not a non-negative int or too big to fit in size uint${this.bitSize}` + ); + } + if (typeof value === 'number' && !Number.isSafeInteger(value)) { + throw new Error( + `${value} should be converted into a BigInt before it is encoded` + ); + } + return bigIntToBytes(value, this.bitSize / 8); + } + + decode(byteString: Uint8Array): bigint { + if (byteString.length !== this.bitSize / 8) { + throw new Error(`byte string must correspond to a uint${this.bitSize}`); + } + return bytesToBigInt(byteString); + } +} + +export class ABIUfixedType extends ABIType { + bitSize: number; + precision: number; + + constructor(size: number, denominator: number) { + super(); + if (size % 8 !== 0 || size < 8 || size > 512) { + throw new Error(`unsupported ufixed type bitSize: ${size}`); + } + if (denominator > 160 || denominator < 1) { + throw new Error(`unsupported ufixed type precision: ${denominator}`); + } + this.bitSize = size; + this.precision = denominator; + } + + toString() { + return `ufixed${this.bitSize}x${this.precision}`; + } + + equals(other: ABIType) { + return ( + other instanceof ABIUfixedType && + this.bitSize === other.bitSize && + this.precision === other.precision + ); + } + + isDynamic() { + return false; + } + + byteLen() { + return this.bitSize / 8; + } + + encode(value: ABIValue) { + if (typeof value !== 'bigint' && typeof value !== 'number') { + throw new Error(`Cannot encode value as ${this.toString()}: ${value}`); + } + if (value >= BigInt(2 ** this.bitSize) || value < BigInt(0)) { + throw new Error( + `${value} is not a non-negative int or too big to fit in size ${this.toString()}` + ); + } + if (typeof value === 'number' && !Number.isSafeInteger(value)) { + throw new Error( + `${value} should be converted into a BigInt before it is encoded` + ); + } + return bigIntToBytes(value, this.bitSize / 8); + } + + decode(byteString: Uint8Array): bigint { + if (byteString.length !== this.bitSize / 8) { + throw new Error(`byte string must correspond to a ${this.toString()}`); + } + return bytesToBigInt(byteString); + } +} + +export class ABIAddressType extends ABIType { + toString() { + return 'address'; + } + + equals(other: ABIType) { + return other instanceof ABIAddressType; + } + + isDynamic() { + return false; + } + + byteLen() { + return ADDR_BYTE_SIZE; + } + + encode(value: ABIValue) { + if (typeof value !== 'string' && !(value instanceof Uint8Array)) { + throw new Error(`Cannot encode value as ${this.toString()}: ${value}`); + } + if (typeof value === 'string') { + const decodedAddress = decodeAddress(value); + return decodedAddress.publicKey; + } + // Return the address if it is already in bytes + if (value.byteLength !== 32) { + throw new Error(`byte string must be 32 bytes long for an address`); + } + return value; + } + + decode(byteString: Uint8Array): string { + if (byteString.byteLength !== 32) { + throw new Error(`byte string must be 32 bytes long for an address`); + } + return encodeAddress(byteString); + } +} + +export class ABIBoolType extends ABIType { + toString() { + return 'bool'; + } + + equals(other: ABIType) { + return other instanceof ABIBoolType; + } + + isDynamic() { + return false; + } + + byteLen() { + return SINGLE_BOOL_SIZE; + } + + encode(value: ABIValue) { + if (typeof value !== 'boolean') { + throw new Error(`Cannot encode value as bool: ${value}`); + } + if (value) { + return new Uint8Array([128]); + } + return new Uint8Array([0]); + } + + decode(byteString: Uint8Array): boolean { + if (byteString.byteLength !== 1) { + throw new Error(`bool string must be 1 byte long`); + } + const value = byteString[0]; + if (value === 128) { + return true; + } + if (value === 0) { + return false; + } + throw new Error(`boolean could not be decoded from the byte string`); + } +} + +export class ABIByteType extends ABIType { + toString() { + return 'byte'; + } + + equals(other: ABIType) { + return other instanceof ABIByteType; + } + + isDynamic() { + return false; + } + + byteLen() { + return SINGLE_BYTE_SIZE; + } + + encode(value: ABIValue) { + if (typeof value !== 'number' && typeof value !== 'bigint') { + throw new Error(`Cannot encode value as byte: ${value}`); + } + if (typeof value === 'bigint') { + // eslint-disable-next-line no-param-reassign + value = Number(value); + } + if (value < 0 || value > 255) { + throw new Error(`${value} cannot be encoded into a byte`); + } + return new Uint8Array([value]); + } + + decode(byteString: Uint8Array): number { + if (byteString.byteLength !== 1) { + throw new Error(`byte string must be 1 byte long`); + } + return byteString[0]; + } +} + +export class ABIStringType extends ABIType { + toString() { + return 'string'; + } + + equals(other: ABIType) { + return other instanceof ABIStringType; + } + + isDynamic() { + return true; + } + + byteLen(): never { + throw new Error(`${this.toString()} is a dynamic type`); + } + + encode(value: ABIValue) { + if (typeof value !== 'string' && !(value instanceof Uint8Array)) { + throw new Error(`Cannot encode value as string: ${value}`); + } + const encodedBytes = Buffer.from(value); + const encodedLength = bigIntToBytes( + encodedBytes.length, + LENGTH_ENCODE_BYTE_SIZE + ); + const mergedBytes = new Uint8Array( + encodedBytes.length + LENGTH_ENCODE_BYTE_SIZE + ); + mergedBytes.set(encodedLength); + mergedBytes.set(encodedBytes, LENGTH_ENCODE_BYTE_SIZE); + return mergedBytes; + } + + decode(byteString: Uint8Array): string { + if (byteString.length < LENGTH_ENCODE_BYTE_SIZE) { + throw new Error( + `byte string is too short to be decoded. Actual length is ${byteString.length}, but expected at least ${LENGTH_ENCODE_BYTE_SIZE}` + ); + } + const buf = Buffer.from(byteString); + const byteLength = buf.readUIntBE(0, LENGTH_ENCODE_BYTE_SIZE); + const byteValue = byteString.slice( + LENGTH_ENCODE_BYTE_SIZE, + byteString.length + ); + if (byteLength !== byteValue.length) { + throw new Error( + `string length bytes do not match the actual length of string. Expected ${byteLength}, got ${byteValue.length}` + ); + } + return Buffer.from(byteValue).toString('utf-8'); + } +} + +export class ABIArrayStaticType extends ABIType { + childType: ABIType; + staticLength: number; + + constructor(argType: ABIType, arrayLength: number) { + super(); + if (arrayLength < 0) { + throw new Error( + `static array must have a non negative length: ${arrayLength}` + ); + } + this.childType = argType; + this.staticLength = arrayLength; + } + + toString() { + return `${this.childType.toString()}[${this.staticLength}]`; + } + + equals(other: ABIType) { + return ( + other instanceof ABIArrayStaticType && + this.staticLength === other.staticLength && + this.childType.equals(other.childType) + ); + } + + isDynamic() { + return this.childType.isDynamic(); + } + + byteLen() { + if (this.childType.constructor === ABIBoolType) { + return Math.ceil(this.staticLength / 8); + } + return this.staticLength * this.childType.byteLen(); + } + + encode(value: ABIValue) { + if (!Array.isArray(value) && !(value instanceof Uint8Array)) { + throw new Error(`Cannot encode value as ${this.toString()}: ${value}`); + } + if (value.length !== this.staticLength) { + throw new Error( + `Value array does not match static array length. Expected ${this.staticLength}, got ${value.length}` + ); + } + const convertedTuple = this.toABITupleType(); + return convertedTuple.encode(value); + } + + decode(byteString: Uint8Array): ABIValue[] { + const convertedTuple = this.toABITupleType(); + return convertedTuple.decode(byteString); + } + + toABITupleType() { + return new ABITupleType(Array(this.staticLength).fill(this.childType)); + } +} + +export class ABIArrayDynamicType extends ABIType { + childType: ABIType; + + constructor(argType: ABIType) { + super(); + this.childType = argType; + } + + toString() { + return `${this.childType.toString()}[]`; + } + + equals(other: ABIType) { + return ( + other instanceof ABIArrayDynamicType && + this.childType.equals(other.childType) + ); + } + + isDynamic() { + return true; + } + + byteLen(): never { + throw new Error(`${this.toString()} is a dynamic type`); + } + + encode(value: ABIValue) { + if (!Array.isArray(value) && !(value instanceof Uint8Array)) { + throw new Error(`Cannot encode value as ${this.toString()}: ${value}`); + } + const convertedTuple = this.toABITupleType(value.length); + const encodedTuple = convertedTuple.encode(value); + const encodedLength = bigIntToBytes( + convertedTuple.childTypes.length, + LENGTH_ENCODE_BYTE_SIZE + ); + const mergedBytes = concatArrays(encodedLength, encodedTuple); + return mergedBytes; + } + + decode(byteString: Uint8Array): ABIValue[] { + const buf = Buffer.from(byteString); + const byteLength = buf.readUIntBE(0, LENGTH_ENCODE_BYTE_SIZE); + const convertedTuple = this.toABITupleType(byteLength); + return convertedTuple.decode( + byteString.slice(LENGTH_ENCODE_BYTE_SIZE, byteString.length) + ); + } + + toABITupleType(length: number) { + return new ABITupleType(Array(length).fill(this.childType)); + } +} + +export class ABITupleType extends ABIType { + childTypes: ABIType[]; + + constructor(argTypes: ABIType[]) { + super(); + if (argTypes.length >= MAX_LEN) { + throw new Error( + 'tuple type child type number larger than maximum uint16 error' + ); + } + this.childTypes = argTypes; + } + + toString() { + const typeStrings: string[] = []; + for (let i = 0; i < this.childTypes.length; i++) { + typeStrings[i] = this.childTypes[i].toString(); + } + return `(${typeStrings.join(',')})`; + } + + equals(other: ABIType) { + return ( + other instanceof ABITupleType && + this.childTypes.length === other.childTypes.length && + this.childTypes.every((child, index) => + child.equals(other.childTypes[index]) + ) + ); + } + + isDynamic() { + const isDynamic = (child: ABIType) => child.isDynamic(); + return this.childTypes.some(isDynamic); + } + + byteLen() { + let size = 0; + for (let i = 0; i < this.childTypes.length; i++) { + if (this.childTypes[i].constructor === ABIBoolType) { + const after = findBoolLR(this.childTypes, i, 1); + const boolNum = after + 1; + i += after; + size += Math.trunc((boolNum + 7) / 8); + } else { + const childByteSize = this.childTypes[i].byteLen(); + size += childByteSize; + } + } + return size; + } + + encode(value: ABIValue) { + if (!Array.isArray(value) && !(value instanceof Uint8Array)) { + throw new Error(`Cannot encode value as ${this.toString()}: ${value}`); + } + const values = Array.from(value); + if (value.length > MAX_LEN) { + throw new Error('length of tuple array should not exceed a uint16'); + } + const tupleTypes = this.childTypes; + const heads: Uint8Array[] = []; + const tails: Uint8Array[] = []; + const isDynamicIndex = new Map(); + let i = 0; + + while (i < tupleTypes.length) { + const tupleType = tupleTypes[i]; + if (tupleType.isDynamic()) { + // Head is not pre-determined for dynamic types; store a placeholder for now + isDynamicIndex.set(heads.length, true); + heads.push(new Uint8Array([0, 0])); + tails.push(tupleType.encode(values[i])); + } else { + if (tupleType.constructor === ABIBoolType) { + const before = findBoolLR(tupleTypes, i, -1); + let after = findBoolLR(tupleTypes, i, 1); + + // Pack bytes to heads and tails + if (before % 8 !== 0) { + throw new Error( + 'expected before index should have number of bool mod 8 equal 0' + ); + } + after = Math.min(7, after); + const compressedInt = compressMultipleBool( + values.slice(i, i + after + 1) + ); + heads.push(bigIntToBytes(compressedInt, 1)); + i += after; + } else { + const encodedTupleValue = tupleType.encode(values[i]); + heads.push(encodedTupleValue); + } + isDynamicIndex.set(i, false); + tails.push(new Uint8Array()); + } + i += 1; + } + + // Adjust head lengths for dynamic types + let headLength = 0; + for (const headElement of heads) { + headLength += headElement.length; + } + + // encode any placeholders for dynamic types + let tailLength = 0; + for (let j = 0; j < heads.length; j++) { + if (isDynamicIndex.get(j)) { + const headValue = headLength + tailLength; + if (headValue > MAX_LEN) { + throw new Error( + `byte length of ${headValue} should not exceed a uint16` + ); + } + heads[j] = bigIntToBytes(headValue, LENGTH_ENCODE_BYTE_SIZE); + } + tailLength += tails[j].length; + } + + return concatArrays(...heads, ...tails); + } + + decode(byteString: Uint8Array): ABIValue[] { + const tupleTypes = this.childTypes; + const dynamicSegments: Segment[] = []; + const valuePartition: Uint8Array[] = []; + let i = 0; + let iterIndex = 0; + const buf = Buffer.from(byteString); + + while (i < tupleTypes.length) { + const tupleType = tupleTypes[i]; + if (tupleType.isDynamic()) { + if ( + byteString.slice(iterIndex, byteString.length).length < + LENGTH_ENCODE_BYTE_SIZE + ) { + throw new Error('dynamic type in tuple is too short to be decoded'); + } + const dynamicIndex = buf.readUIntBE(iterIndex, LENGTH_ENCODE_BYTE_SIZE); + if (dynamicSegments.length > 0) { + dynamicSegments[dynamicSegments.length - 1].right = dynamicIndex; + // Check that right side of segment is greater than the left side + if (dynamicIndex < dynamicSegments[dynamicSegments.length - 1].left) { + throw new Error( + 'dynamic index segment miscalculation: left is greater than right index' + ); + } + } + // Since we do not know where the current dynamic element ends, put a placeholder and update later + const seg: Segment = { + left: dynamicIndex, + right: -1, + }; + dynamicSegments.push(seg); + valuePartition.push(null); + iterIndex += LENGTH_ENCODE_BYTE_SIZE; + } else { + // eslint-disable-next-line no-lonely-if + if (tupleType.constructor === ABIBoolType) { + const before = findBoolLR(this.childTypes, i, -1); + let after = findBoolLR(this.childTypes, i, 1); + + if (before % 8 !== 0) { + throw new Error('expected before bool number mod 8 === 0'); + } + after = Math.min(7, after); + // Parse bool in a byte to multiple byte strings + for (let boolIndex = 0; boolIndex <= after; boolIndex++) { + const boolMask = 0x80 >> boolIndex; + if ((byteString[iterIndex] & boolMask) > 0) { + valuePartition.push(new Uint8Array([128])); + } else { + valuePartition.push(new Uint8Array([0])); + } + } + i += after; + iterIndex += 1; + } else { + const currLen = tupleType.byteLen(); + valuePartition.push(byteString.slice(iterIndex, iterIndex + currLen)); + iterIndex += currLen; + } + } + if (i !== tupleTypes.length - 1 && iterIndex >= byteString.length) { + throw new Error('input byte not enough to decode'); + } + i += 1; + } + if (dynamicSegments.length > 0) { + dynamicSegments[dynamicSegments.length - 1].right = byteString.length; + iterIndex = byteString.length; + } + if (iterIndex < byteString.length) { + throw new Error('input byte not fully consumed'); + } + + // Check segment indices are valid + // If the dynamic segment are not consecutive and well-ordered, we return error + for (let j = 0; j < dynamicSegments.length; j++) { + const seg = dynamicSegments[j]; + if (seg.left > seg.right) { + throw new Error( + 'dynamic segment should display a [l, r] space with l <= r' + ); + } + if ( + j !== dynamicSegments.length - 1 && + seg.right !== dynamicSegments[j + 1].left + ) { + throw new Error('dynamic segment should be consecutive'); + } + } + + // Check dynamic element partitions + let segIndex = 0; + for (let j = 0; j < tupleTypes.length; j++) { + if (tupleTypes[j].isDynamic()) { + valuePartition[j] = byteString.slice( + dynamicSegments[segIndex].left, + dynamicSegments[segIndex].right + ); + segIndex += 1; + } + } + + // Decode each tuple element + const returnValues: ABIValue[] = []; + for (let j = 0; j < tupleTypes.length; j++) { + const valueTi = tupleTypes[j].decode(valuePartition[j]); + returnValues.push(valueTi); + } + return returnValues; + } + + static parseTupleContent(str: string): string[] { + if (str.length === 0) { + return []; + } + if (str.endsWith(',') || str.startsWith(',')) { + throw new Error('tuple string should not start with comma'); + } + if (str.includes(',,')) { + throw new Error('tuple string should not have consecutive commas'); + } + + const tupleStrings: string[] = []; + let depth = 0; + let word = ''; + + for (const char of str) { + word += char; + if (char === '(') { + depth += 1; + } else if (char === ')') { + depth -= 1; + } else if (char === ',') { + // If the comma is at depth 0, then append the word as token. + if (depth === 0) { + tupleStrings.push(word.slice(0, word.length - 1)); + word = ''; + } + } + } + if (word.length !== 0) { + tupleStrings.push(word); + } + if (depth !== 0) { + throw new Error('tuple string has mismatched parentheses'); + } + return tupleStrings; + } +} + +// compressMultipleBool compresses consecutive bool values into a byte in ABI tuple / array value. +function compressMultipleBool(valueList: ABIValue[]): number { + let res = 0; + if (valueList.length > 8) { + throw new Error('value list passed in should be no greater than length 8'); + } + for (let i = 0; i < valueList.length; i++) { + const boolVal = valueList[i]; + if (typeof boolVal !== 'boolean') { + throw new Error('non-boolean values cannot be compressed into a byte'); + } + if (boolVal) { + res |= 1 << (7 - i); + } + } + return res; +} + +// Assume that the current index on the list of type is an ABI bool type. +// It returns the difference between the current index and the index of the furthest consecutive Bool type. +function findBoolLR(typeList: ABIType[], index: number, delta: -1 | 1): number { + let until = 0; + while (true) { + const curr = index + delta * until; + if (typeList[curr].constructor === ABIBoolType) { + if (curr !== typeList.length - 1 && delta === 1) { + until += 1; + } else if (curr > 0 && delta === -1) { + until += 1; + } else { + break; + } + } else { + until -= 1; + break; + } + } + return until; +} diff --git a/src/abi/contract.ts b/src/abi/contract.ts new file mode 100644 index 0000000..d4d6236 --- /dev/null +++ b/src/abi/contract.ts @@ -0,0 +1,51 @@ +import { ABIMethod, ABIMethodParams, getMethodByName } from './method'; + +export interface ABIContractNetworkInfo { + appID: number; +} + +export interface ABIContractNetworks { + [network: string]: ABIContractNetworkInfo; +} + +export interface ABIContractParams { + name: string; + desc?: string; + networks?: ABIContractNetworks; + methods: ABIMethodParams[]; +} + +export class ABIContract { + public readonly name: string; + public readonly description?: string; + public readonly networks: ABIContractNetworks; + public readonly methods: ABIMethod[]; + + constructor(params: ABIContractParams) { + if ( + typeof params.name !== 'string' || + !Array.isArray(params.methods) || + (params.networks && typeof params.networks !== 'object') + ) { + throw new Error('Invalid ABIContract parameters'); + } + + this.name = params.name; + this.description = params.desc; + this.networks = params.networks ? { ...params.networks } : {}; + this.methods = params.methods.map((method) => new ABIMethod(method)); + } + + toJSON(): ABIContractParams { + return { + name: this.name, + desc: this.description, + networks: this.networks, + methods: this.methods.map((method) => method.toJSON()), + }; + } + + getMethodByName(name: string): ABIMethod { + return getMethodByName(this.methods, name); + } +} diff --git a/src/abi/index.ts b/src/abi/index.ts new file mode 100644 index 0000000..01c18da --- /dev/null +++ b/src/abi/index.ts @@ -0,0 +1,6 @@ +export * from './abi_type'; +export * from './contract'; +export * from './interface'; +export * from './method'; +export * from './transaction'; +export * from './reference'; diff --git a/src/abi/interface.ts b/src/abi/interface.ts new file mode 100644 index 0000000..b676c5d --- /dev/null +++ b/src/abi/interface.ts @@ -0,0 +1,35 @@ +import { ABIMethod, ABIMethodParams, getMethodByName } from './method'; + +export interface ABIInterfaceParams { + name: string; + desc?: string; + methods: ABIMethodParams[]; +} + +export class ABIInterface { + public readonly name: string; + public readonly description?: string; + public readonly methods: ABIMethod[]; + + constructor(params: ABIInterfaceParams) { + if (typeof params.name !== 'string' || !Array.isArray(params.methods)) { + throw new Error('Invalid ABIInterface parameters'); + } + + this.name = params.name; + this.description = params.desc; + this.methods = params.methods.map((method) => new ABIMethod(method)); + } + + toJSON(): ABIInterfaceParams { + return { + name: this.name, + desc: this.description, + methods: this.methods.map((method) => method.toJSON()), + }; + } + + getMethodByName(name: string): ABIMethod { + return getMethodByName(this.methods, name); + } +} diff --git a/src/abi/method.ts b/src/abi/method.ts new file mode 100644 index 0000000..00704ef --- /dev/null +++ b/src/abi/method.ts @@ -0,0 +1,186 @@ +import { genericHash } from '../nacl/naclWrappers'; +import { ABIType, ABITupleType } from './abi_type'; +import { ABITransactionType, abiTypeIsTransaction } from './transaction'; +import { ABIReferenceType, abiTypeIsReference } from './reference'; + +function parseMethodSignature( + signature: string +): { name: string; args: string[]; returns: string } { + const argsStart = signature.indexOf('('); + if (argsStart === -1) { + throw new Error(`Invalid method signature: ${signature}`); + } + + let argsEnd = -1; + let depth = 0; + for (let i = argsStart; i < signature.length; i++) { + const char = signature[i]; + + if (char === '(') { + depth += 1; + } else if (char === ')') { + if (depth === 0) { + // unpaired parenthesis + break; + } + + depth -= 1; + if (depth === 0) { + argsEnd = i; + break; + } + } + } + + if (argsEnd === -1) { + throw new Error(`Invalid method signature: ${signature}`); + } + + return { + name: signature.slice(0, argsStart), + args: ABITupleType.parseTupleContent( + signature.slice(argsStart + 1, argsEnd) + ), + returns: signature.slice(argsEnd + 1), + }; +} + +export interface ABIMethodArgParams { + type: string; + name?: string; + desc?: string; +} + +export interface ABIMethodReturnParams { + type: string; + desc?: string; +} + +export interface ABIMethodParams { + name: string; + desc?: string; + args: ABIMethodArgParams[]; + returns: ABIMethodReturnParams; +} + +export type ABIArgumentType = ABIType | ABITransactionType | ABIReferenceType; + +export type ABIReturnType = ABIType | 'void'; + +export class ABIMethod { + public readonly name: string; + public readonly description?: string; + public readonly args: Array<{ + type: ABIArgumentType; + name?: string; + description?: string; + }>; + + public readonly returns: { type: ABIReturnType; description?: string }; + + constructor(params: ABIMethodParams) { + if ( + typeof params.name !== 'string' || + typeof params.returns !== 'object' || + !Array.isArray(params.args) + ) { + throw new Error('Invalid ABIMethod parameters'); + } + + this.name = params.name; + this.description = params.desc; + this.args = params.args.map(({ type, name, desc }) => { + if (abiTypeIsTransaction(type) || abiTypeIsReference(type)) { + return { + type, + name, + description: desc, + }; + } + + return { + type: ABIType.from(type), + name, + description: desc, + }; + }); + this.returns = { + type: + params.returns.type === 'void' + ? params.returns.type + : ABIType.from(params.returns.type), + description: params.returns.desc, + }; + } + + getSignature(): string { + const args = this.args.map((arg) => arg.type.toString()).join(','); + const returns = this.returns.type.toString(); + return `${this.name}(${args})${returns}`; + } + + getSelector(): Uint8Array { + const hash = genericHash(this.getSignature()); + return new Uint8Array(hash.slice(0, 4)); + } + + txnCount(): number { + let count = 1; + for (const arg of this.args) { + if (typeof arg.type === 'string' && abiTypeIsTransaction(arg.type)) { + count += 1; + } + } + return count; + } + + toJSON(): ABIMethodParams { + return { + name: this.name, + desc: this.description, + args: this.args.map(({ type, name, description }) => ({ + type: type.toString(), + name, + desc: description, + })), + returns: { + type: this.returns.type.toString(), + desc: this.returns.description, + }, + }; + } + + static fromSignature(signature: string): ABIMethod { + const { name, args, returns } = parseMethodSignature(signature); + + return new ABIMethod({ + name, + args: args.map((arg) => ({ type: arg })), + returns: { type: returns }, + }); + } +} + +export function getMethodByName(methods: ABIMethod[], name: string): ABIMethod { + if ( + methods === null || + !Array.isArray(methods) || + !methods.every((item) => item instanceof ABIMethod) + ) + throw new Error('Methods list provided is null or not the correct type'); + + const filteredMethods = methods.filter((m: ABIMethod) => m.name === name); + if (filteredMethods.length > 1) + throw new Error( + `found ${ + filteredMethods.length + } methods with the same name ${filteredMethods + .map((m: ABIMethod) => m.getSignature()) + .join(',')}` + ); + + if (filteredMethods.length === 0) + throw new Error(`found 0 methods with the name ${name}`); + + return filteredMethods[0]; +} diff --git a/src/abi/reference.ts b/src/abi/reference.ts new file mode 100644 index 0000000..c3e0234 --- /dev/null +++ b/src/abi/reference.ts @@ -0,0 +1,24 @@ +export enum ABIReferenceType { + /** + * Account reference type + */ + account = 'account', + + /** + * Application reference type + */ + application = 'application', + + /** + * Asset reference type + */ + asset = 'asset', +} + +export function abiTypeIsReference(type: any): type is ABIReferenceType { + return ( + type === ABIReferenceType.account || + type === ABIReferenceType.application || + type === ABIReferenceType.asset + ); +} diff --git a/src/abi/transaction.ts b/src/abi/transaction.ts new file mode 100644 index 0000000..880250f --- /dev/null +++ b/src/abi/transaction.ts @@ -0,0 +1,61 @@ +import { Transaction } from '../transaction'; + +export enum ABITransactionType { + /** + * Any transaction type + */ + any = 'txn', + + /** + * Payment transaction type + */ + pay = 'pay', + + /** + * Key registration transaction type + */ + keyreg = 'keyreg', + + /** + * Asset configuration transaction type + */ + acfg = 'acfg', + + /** + * Asset transfer transaction type + */ + axfer = 'axfer', + + /** + * Asset freeze transaction type + */ + afrz = 'afrz', + + /** + * Application transaction type + */ + appl = 'appl', +} + +export function abiTypeIsTransaction(type: any): type is ABITransactionType { + return ( + type === ABITransactionType.any || + type === ABITransactionType.pay || + type === ABITransactionType.keyreg || + type === ABITransactionType.acfg || + type === ABITransactionType.axfer || + type === ABITransactionType.afrz || + type === ABITransactionType.appl + ); +} + +export function abiCheckTransactionType( + type: ABITransactionType, + txn: Transaction +): boolean { + if (type === ABITransactionType.any) { + return true; + } + + return txn.type && txn.type.toString() === type.toString(); +} diff --git a/src/account.ts b/src/account.ts new file mode 100644 index 0000000..b6756c4 --- /dev/null +++ b/src/account.ts @@ -0,0 +1,12 @@ +import * as nacl from './nacl/naclWrappers'; +import * as address from './encoding/address'; +import Account from './types/account'; + +/** + * generateAccount returns a new Algorand address and its corresponding secret key + */ +export default function generateAccount(): Account { + const keys = nacl.keyPair(); + const encodedPk = address.encodeAddress(keys.publicKey); + return { addr: encodedPk, sk: keys.secretKey }; +} diff --git a/src/bid.ts b/src/bid.ts new file mode 100644 index 0000000..7874d64 --- /dev/null +++ b/src/bid.ts @@ -0,0 +1,96 @@ +import { Buffer } from 'buffer'; +import * as address from './encoding/address'; +import * as encoding from './encoding/encoding'; +import * as nacl from './nacl/naclWrappers'; +import * as utils from './utils/utils'; +import { Address } from './types/address'; + +interface BidStorageStructure { + bidderKey: Address; + bidAmount: number; + bidID: number; + auctionKey: Address; + auctionID: number; + maxPrice: number; +} + +export type BidOptions = Omit< + BidStorageStructure, + 'bidderKey' | 'auctionKey' +> & { + bidderKey: string; + auctionKey: string; +}; + +/** + * Bid enables construction of Algorand Auctions Bids + * */ +export default class Bid implements BidStorageStructure { + name = 'Bid'; + tag = Buffer.from([97, 66]); // "aB" + + bidderKey: Address; + bidAmount: number; + bidID: number; + auctionKey: Address; + auctionID: number; + maxPrice: number; + + constructor({ + bidderKey, + bidAmount, + bidID, + auctionKey, + auctionID, + maxPrice, + }: BidOptions) { + const decodedBidderKey = address.decodeAddress(bidderKey); + const decodedAuctionKey = address.decodeAddress(auctionKey); + + if (!Number.isSafeInteger(bidAmount) || bidAmount < 0) + throw Error('Bid amount must be positive and 2^53-1'); + if (!Number.isSafeInteger(bidID) || bidID < 0) + throw Error('BidID must be positive and 2^53-1'); + if (!Number.isSafeInteger(auctionID) || auctionID < 0) + throw Error('auctionID must be positive'); + + Object.assign(this, { + bidderKey: decodedBidderKey, + bidAmount, + bidID, + auctionKey: decodedAuctionKey, + auctionID, + maxPrice, + }); + } + + // eslint-disable-next-line camelcase + get_obj_for_encoding() { + return { + bidder: Buffer.from(this.bidderKey.publicKey), + cur: this.bidAmount, + price: this.maxPrice, + id: this.bidID, + auc: Buffer.from(this.auctionKey.publicKey), + aid: this.auctionID, + }; + } + + signBid(sk: Uint8Array) { + const encodedMsg = encoding.encode(this.get_obj_for_encoding()); + const toBeSigned = Buffer.from(utils.concatArrays(this.tag, encodedMsg)); + const sig = nacl.sign(toBeSigned, sk); + + // construct signed message + const sBid = { + sig: Buffer.from(sig), + bid: this.get_obj_for_encoding(), + }; + + const note = { + t: 'b', + b: sBid, + }; + return new Uint8Array(encoding.encode(note)); + } +} diff --git a/src/boxStorage.ts b/src/boxStorage.ts new file mode 100644 index 0000000..05e9948 --- /dev/null +++ b/src/boxStorage.ts @@ -0,0 +1,42 @@ +import { EncodedBoxReference } from './types'; +import { BoxReference } from './types/transactions/base'; + +function translateBoxReference( + reference: BoxReference, + foreignApps: number[], + appIndex: number +): EncodedBoxReference { + const referenceId = reference.appIndex; + const referenceName = reference.name; + const isOwnReference = referenceId === 0 || referenceId === appIndex; + let index = 0; + + if (foreignApps != null) { + // Foreign apps start from index 1; index 0 is its own app ID. + index = foreignApps.indexOf(referenceId) + 1; + } + // Check if the app referenced is itself after checking the foreign apps array. + // If index is zero, then the app ID was not found in the foreign apps array + // or the foreign apps array was null. + if (index === 0 && !isOwnReference) { + // Error if the app is trying to reference a foreign app that was not in + // its own foreign apps array. + throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); + } + return { i: index, n: referenceName }; +} + +/** + * translateBoxReferences translates an array of BoxReferences with app IDs + * into an array of EncodedBoxReferences with foreign indices. + */ +export function translateBoxReferences( + references: BoxReference[] | undefined, + foreignApps: number[], + appIndex: number +): EncodedBoxReference[] { + if (references == null) return []; + return references.map((bx) => + translateBoxReference(bx, foreignApps, appIndex) + ); +} diff --git a/src/client/baseHTTPClient.ts b/src/client/baseHTTPClient.ts new file mode 100644 index 0000000..39b1af6 --- /dev/null +++ b/src/client/baseHTTPClient.ts @@ -0,0 +1,58 @@ +export type Query = { + format?: F; + [key: string]: any; +}; + +export interface BaseHTTPClientResponse { + body: Uint8Array; + status: number; // status must always be 200 except when the response is inside an error + headers: Record; +} + +/** + * BaseHTTPClientError is the interface that errors thrown + * by methods of BaseHTTPClient should be using + */ +export interface BaseHTTPClientError { + response: BaseHTTPClientResponse; +} + +/** + * BaseHTTPClient is an interface abstracting the queries that can be + * made to an algod/indexer endpoint. + * The SDK normally uses the URLTokenBaseHTTPClient implementation. + * But when used via wallets, the wallet may provide a different object + * satisfying the HTTPClient interface. This is useful to allow + * wallets to provide access to paid API services without leaking + * the secret tokens/URLs. + * + * Note that post and delete also have an optional query parameter + * This is to allow future extension where post and delete may have queries + * Currently however HTTPClient does not make use of it + * + * Compared to HTTPClient, BaseHTTPClient does not deal with serialization/deserialization + * Everything is already string/Uint8Array + * and all the headers (including Accept/Content-Type) are assumed to be provided + * + * In case of non-200 status, all methods must throw an error of type + * BaseHTTPClientError + */ +export interface BaseHTTPClient { + get( + relativePath: string, + query?: Query, + requestHeaders?: Record + ): Promise; + post( + relativePath: string, + data: Uint8Array, + query?: Query, + requestHeaders?: Record + ): Promise; + delete( + relativePath: string, + data: Uint8Array, + query?: Query, + requestHeaders?: Record + ): Promise; +} diff --git a/src/client/client.ts b/src/client/client.ts new file mode 100644 index 0000000..6a20366 --- /dev/null +++ b/src/client/client.ts @@ -0,0 +1,307 @@ +import { Buffer } from 'buffer'; +import * as utils from '../utils/utils'; +import { + BaseHTTPClient, + BaseHTTPClientResponse, + Query, +} from './baseHTTPClient'; +import { TokenHeader, URLTokenBaseHTTPClient } from './urlTokenBaseHTTPClient'; + +interface ErrorWithAdditionalInfo extends Error { + rawResponse: string | null; + statusCode: number; +} + +export interface HTTPClientResponse { + body: Uint8Array | any; // when content-type=JSON, body is a JSON object, otherwise it's a Uint8Array + text?: string; + headers: Record; + status: number; + ok: boolean; +} + +/** + * Remove falsy values or values with a length of 0 from an object. + */ +function removeFalsyOrEmpty(obj: Record) { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + // eslint-disable-next-line no-param-reassign + if (!obj[key] || obj[key].length === 0) delete obj[key]; + } + } + return obj; +} + +/** + * Create a new object with lower-case keys + * See https://codereview.stackexchange.com/a/162418 + * Used to ensure all headers are lower-case and to work more easily with them + */ +function tolowerCaseKeys(o: object): object { + /* eslint-disable no-param-reassign,no-return-assign,no-sequences */ + return Object.keys(o).reduce((c, k) => ((c[k.toLowerCase()] = o[k]), c), {}); + /* eslint-enable no-param-reassign,no-return-assign,no-sequences */ +} + +/** + * getAcceptFormat returns the correct Accept header depending on the + * requested format. + */ +function getAcceptFormat( + query?: Query<'msgpack' | 'json'> +): 'application/msgpack' | 'application/json' { + if ( + query !== undefined && + Object.prototype.hasOwnProperty.call(query, 'format') + ) { + switch (query.format) { + case 'msgpack': + return 'application/msgpack'; + case 'json': + default: + return 'application/json'; + } + } else return 'application/json'; +} + +/** + * HTTPClient is a wrapper around a BaseHTTPClient + * It takes care of setting the proper "Accept" header and of + * decoding the JSON outputs. + */ +export default class HTTPClient { + private bc: BaseHTTPClient; + + /** + * Construct an HTTPClient from a BaseHTTPClient + * @param bc - the BaseHTTPClient used + */ + constructor(bc: BaseHTTPClient); + /** + * Construct an HTTPClient from a URL (baseServer+port) and a token + */ + constructor( + tokenHeader: TokenHeader, + baseServer: string, + port?: string | number, + defaultHeaders?: Record + ); + + constructor( + bcOrTokenHeader: BaseHTTPClient | TokenHeader, + baseServer?: string, + port?: string | number, + defaultHeaders: Record = {} + ) { + if (baseServer !== undefined) { + this.bc = new URLTokenBaseHTTPClient( + bcOrTokenHeader as TokenHeader, + baseServer, + port, + defaultHeaders + ); + } else { + this.bc = bcOrTokenHeader as BaseHTTPClient; + } + } + + /** + * Parse JSON using either the built-in JSON.parse or utils.parseJSON + * depending on whether jsonOptions are provided or not + * + * @param text - JSON data + * @param status - Status of the response (used in case parseJSON fails) + * @param jsonOptions - Options object to use to decode JSON responses. See + * utils.parseJSON for the options available. + */ + public static parseJSON( + text: string, + status: number, + jsonOptions: utils.JSONOptions = {} + ) { + try { + if (Object.keys(jsonOptions).length === 0) { + return text && JSON.parse(text); + } + return text && utils.parseJSON(text, jsonOptions); + } catch (err_) { + const err: ErrorWithAdditionalInfo = err_; + // return the raw response if the response parsing fails + err.rawResponse = text || null; + // return the http status code if the response parsing fails + err.statusCode = status; + throw err; + } + } + + /** + * Serialize the data according to the requestHeaders + * Assumes that requestHeaders contain a key "content-type" + * If the content-type is "application/json", data is JSON serialized + * Otherwise, data needs to be either an UTF-8 string that is converted to an Uint8Array + * or an Uint8Array + * @private + */ + private static serializeData( + data: object, + requestHeaders: Record + ): Uint8Array { + if (!data) { + return new Uint8Array(0); // empty Uint8Array + } + if (requestHeaders['content-type'] === 'application/json') { + return new Uint8Array(Buffer.from(JSON.stringify(data))); + } + if (typeof data === 'string') { + return new Uint8Array(Buffer.from(data)); + } + if (data instanceof Uint8Array) { + return data; + } + throw new Error( + 'provided data is neither a string nor a Uint8Array and content-type is not application/json' + ); + } + + /** + * Convert a BaseHTTPClientResponse into a full HTTPClientResponse + * Parse the body in + * Modifies in place res and return the result + */ + private static prepareResponse( + res: BaseHTTPClientResponse, + format: 'application/msgpack' | 'application/json', + parseBody: boolean, + jsonOptions: utils.JSONOptions = {} + ): HTTPClientResponse { + let { body } = res; + let text; + + if (format !== 'application/msgpack') { + text = (body && Buffer.from(body).toString()) || ''; + } + + if (parseBody && format === 'application/json') { + body = HTTPClient.parseJSON(text, res.status, jsonOptions); + } + + return { + ...res, + body, + text, + ok: Math.trunc(res.status / 100) === 2, + }; + } + + /** + * Prepare an error with a response + * (the type of errors BaseHTTPClient are supposed to throw) + * by adding the status and preparing the internal response + * @private + */ + private static prepareResponseError(err) { + if (err.response) { + // eslint-disable-next-line no-param-reassign + err.response = HTTPClient.prepareResponse( + err.response, + 'application/json', + true + ); + // eslint-disable-next-line no-param-reassign + err.status = err.response.status; + } + return err; + } + + /** + * Send a GET request. + * @param relativePath - The path of the request. + * @param query - An object containing the query parameters of the request. + * @param requestHeaders - An object containing additional request headers to use. + * @param jsonOptions - Options object to use to decode JSON responses. See + * utils.parseJSON for the options available. + * @param parseBody - An optional boolean indicating whether the response body should be parsed + * or not. + * @returns Response object. + */ + async get( + relativePath: string, + query?: Query, + requestHeaders: Record = {}, + jsonOptions: utils.JSONOptions = {}, + parseBody: boolean = true + ): Promise { + const format = getAcceptFormat(query); + const fullHeaders = { ...requestHeaders, accept: format }; + + try { + const res = await this.bc.get( + relativePath, + removeFalsyOrEmpty(query), + fullHeaders + ); + + return HTTPClient.prepareResponse(res, format, parseBody, jsonOptions); + } catch (err) { + throw HTTPClient.prepareResponseError(err); + } + } + + /** + * Send a POST request. + * If no content-type present, adds the header "content-type: application/json" + * and data is serialized in JSON (if not empty) + */ + async post( + relativePath: string, + data: any, + requestHeaders: Record = {}, + query?: Query, + parseBody: boolean = true + ): Promise { + const fullHeaders = { + 'content-type': 'application/json', + ...tolowerCaseKeys(requestHeaders), + }; + + try { + const res = await this.bc.post( + relativePath, + HTTPClient.serializeData(data, fullHeaders), + query, + fullHeaders + ); + + return HTTPClient.prepareResponse(res, 'application/json', parseBody); + } catch (err) { + throw HTTPClient.prepareResponseError(err); + } + } + + /** + * Send a DELETE request. + * If no content-type present, adds the header "content-type: application/json" + * and data is serialized in JSON (if not empty) + */ + async delete( + relativePath: string, + data: any, + requestHeaders: Record = {}, + parseBody: boolean = true + ) { + const fullHeaders = { + 'content-type': 'application/json', + ...tolowerCaseKeys(requestHeaders), + }; + + const res = await this.bc.delete( + relativePath, + HTTPClient.serializeData(data, fullHeaders), + undefined, + fullHeaders + ); + + return HTTPClient.prepareResponse(res, 'application/json', parseBody); + } +} diff --git a/src/client/kmd.ts b/src/client/kmd.ts new file mode 100644 index 0000000..4c832b2 --- /dev/null +++ b/src/client/kmd.ts @@ -0,0 +1,424 @@ +import { Buffer } from 'buffer'; +import ServiceClient from './v2/serviceClient'; +import * as txn from '../transaction'; +import { CustomTokenHeader, KMDTokenHeader } from './urlTokenBaseHTTPClient'; + +export default class Kmd extends ServiceClient { + constructor( + token: string | KMDTokenHeader | CustomTokenHeader, + baseServer = 'http://127.0.0.1', + port: string | number = 7833, + headers = {} + ) { + super('X-KMD-API-Token', token, baseServer, port, headers); + } + + /** + * version returns a VersionResponse containing a list of kmd API versions supported by this running kmd instance. + */ + async versions() { + const res = await this.c.get('/versions'); + return res.body; + } + + /** + * listWallets returns a ListWalletsResponse containing the list of wallets known to kmd. Using a wallet ID + * returned from this endpoint, you can initialize a wallet handle with client.InitWalletHandle + */ + async listWallets() { + const res = await this.c.get('/v1/wallets'); + return res.body; + } + + /** + * createWallet creates a wallet with the specified name, password, driver, + * and master derivation key. If the master derivation key is blank, one is + * generated internally to kmd. CreateWallet returns a CreateWalletResponse + * containing information about the new wallet. + * @param walletName + * @param walletPassword + * @param walletDriverName + * @param walletMDK + */ + async createWallet( + walletName: string, + walletPassword: string, + walletMDK: Uint8Array = new Uint8Array(), + walletDriverName = 'sqlite' + ) { + const req = { + wallet_name: walletName, + wallet_driver_name: walletDriverName, + wallet_password: walletPassword, + master_derivation_key: Buffer.from(walletMDK).toString('base64'), + }; + const res = await this.c.post('/v1/wallet', req); + return res.body; + } + + /** + * initWalletHandle accepts a wallet ID and a wallet password, and returns an + * initWalletHandleResponse containing a wallet handle token. This wallet + * handle token can be used for subsequent operations on this wallet, like key + * generation, transaction signing, etc.. WalletHandleTokens expire after a + * configurable number of seconds, and must be renewed periodically with + * RenewWalletHandle. It is good practice to call ReleaseWalletHandle when + * you're done interacting with this wallet. + * @param walletID + * @param walletPassword + */ + async initWalletHandle(walletID: string, walletPassword: string) { + const req = { + wallet_id: walletID, + wallet_password: walletPassword, + }; + const res = await this.c.post('/v1/wallet/init', req); + return res.body; + } + + /** + * releaseWalletHandle invalidates the passed wallet handle token, making + * it unusuable for subsequent wallet operations. + * @param walletHandle + */ + async releaseWalletHandle(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + }; + const res = await this.c.post('/v1/wallet/release', req); + return res.body; + } + + /** + * renewWalletHandle accepts a wallet handle and attempts to renew it, moving + * the expiration time to some number of seconds in the future. It returns a + * RenewWalletHandleResponse containing the walletHandle and the number of + * seconds until expiration + * @param walletHandle + */ + async renewWalletHandle(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + }; + const res = await this.c.post('/v1/wallet/renew', req); + return res.body; + } + + /** + * renameWallet accepts a wallet ID, wallet password, and a new wallet name, + * and renames the underlying wallet. + * @param walletID + * @param walletPassword + * @param newWalletName + */ + async renameWallet( + walletID: string, + walletPassword: string, + newWalletName: string + ) { + const req = { + wallet_id: walletID, + wallet_password: walletPassword, + wallet_name: newWalletName, + }; + const res = await this.c.post('/v1/wallet/rename', req); + return res.body; + } + + /** + * getWallet accepts a wallet handle and returns high level information about + * this wallet in a GetWalletResponse. + * @param walletHandle + */ + async getWallet(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + }; + const res = await this.c.post('/v1/wallet/info', req); + return res.body; + } + + /** + * exportMasterDerivationKey accepts a wallet handle and a wallet password, and + * returns an ExportMasterDerivationKeyResponse containing the master + * derivation key. This key can be used as an argument to CreateWallet in + * order to recover the keys generated by this wallet. The master derivation + * key can be encoded as a sequence of words using the mnemonic library, and + * @param walletHandle + * @param walletPassword + */ + async exportMasterDerivationKey( + walletHandle: string, + walletPassword: string + ) { + const req = { + wallet_handle_token: walletHandle, + wallet_password: walletPassword, + }; + const res = await this.c.post('/v1/master-key/export', req); + return { + master_derivation_key: Buffer.from( + res.body.master_derivation_key, + 'base64' + ), + }; + } + + /** + * importKey accepts a wallet handle and an ed25519 private key, and imports + * the key into the wallet. It returns an ImportKeyResponse containing the + * address corresponding to this private key. + * @param walletHandle + * @param secretKey + */ + async importKey(walletHandle: string, secretKey: Uint8Array) { + const req = { + wallet_handle_token: walletHandle, + private_key: Buffer.from(secretKey).toString('base64'), + }; + const res = await this.c.post('/v1/key/import', req); + return res.body; + } + + /** + * exportKey accepts a wallet handle, wallet password, and address, and returns + * an ExportKeyResponse containing the ed25519 private key corresponding to the + * address stored in the wallet. + * @param walletHandle + * @param walletPassword + * @param addr + */ + async exportKey(walletHandle: string, walletPassword: string, addr: string) { + const req = { + wallet_handle_token: walletHandle, + address: addr, + wallet_password: walletPassword, + }; + const res = await this.c.post('/v1/key/export', req); + return { private_key: Buffer.from(res.body.private_key, 'base64') }; + } + + /** + * generateKey accepts a wallet handle, and then generates the next key in the + * wallet using its internal master derivation key. Two wallets with the same + * master derivation key will generate the same sequence of keys. + * @param walletHandle + */ + async generateKey(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + display_mnemonic: false, + }; + const res = await this.c.post('/v1/key', req); + return res.body; + } + + /** + * deleteKey accepts a wallet handle, wallet password, and address, and deletes + * the information about this address from the wallet (including address and + * secret key). If DeleteKey is called on a key generated using GenerateKey, + * the same key will not be generated again. However, if a wallet is recovered + * using the master derivation key, a key generated in this way can be + * recovered. + * @param walletHandle + * @param walletPassword + * @param addr + */ + async deleteKey(walletHandle: string, walletPassword: string, addr: string) { + const req = { + wallet_handle_token: walletHandle, + address: addr, + wallet_password: walletPassword, + }; + const res = await this.c.delete('/v1/key', req); + return res.body; + } + + /** + * ListKeys accepts a wallet handle and returns a ListKeysResponse containing + * all of the addresses for which this wallet contains secret keys. + * @param walletHandle + */ + async listKeys(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + }; + const res = await this.c.post('/v1/key/list', req); + return res.body; + } + + /** + * signTransaction accepts a wallet handle, wallet password, and a transaction, + * and returns and SignTransactionResponse containing an encoded, signed + * transaction. The transaction is signed using the key corresponding to the + * Sender field. + * @param walletHandle + * @param walletPassword + * @param transaction + */ + async signTransaction( + walletHandle: string, + walletPassword: string, + transaction: txn.TransactionLike + ) { + const tx = txn.instantiateTxnIfNeeded(transaction); + + const req = { + wallet_handle_token: walletHandle, + wallet_password: walletPassword, + transaction: Buffer.from(tx.toByte()).toString('base64'), + }; + const res = await this.c.post('/v1/transaction/sign', req); + + if (res.status === 200) { + return Buffer.from(res.body.signed_transaction, 'base64'); + } + return res.body; + } + + /** + * signTransactionWithSpecificPublicKey accepts a wallet handle, wallet password, a transaction, and a public key, + * and returns and SignTransactionResponse containing an encoded, signed + * transaction. The transaction is signed using the key corresponding to the + * publicKey arg. + * @param walletHandle + * @param walletPassword + * @param transaction + * @param publicKey - sign the txn with the key corresponding to publicKey (used for working with a rekeyed addr) + */ + async signTransactionWithSpecificPublicKey( + walletHandle: string, + walletPassword: string, + transaction: txn.TransactionLike, + publicKey: Uint8Array | string + ) { + const tx = txn.instantiateTxnIfNeeded(transaction); + + const req = { + wallet_handle_token: walletHandle, + wallet_password: walletPassword, + transaction: Buffer.from(tx.toByte()).toString('base64'), + public_key: Buffer.from(publicKey).toString('base64'), + }; + const res = await this.c.post('/v1/transaction/sign', req); + + if (res.status === 200) { + return Buffer.from(res.body.signed_transaction, 'base64'); + } + return res.body; + } + + /** + * listMultisig accepts a wallet handle and returns a ListMultisigResponse + * containing the multisig addresses whose preimages are stored in this wallet. + * A preimage is the information needed to reconstruct this multisig address, + * including multisig version information, threshold information, and a list + * of public keys. + * @param walletHandle + */ + async listMultisig(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + }; + const res = await this.c.post('/v1/multisig/list', req); + return res.body; + } + + /** + * importMultisig accepts a wallet handle and the information required to + * generate a multisig address. It derives this address, and stores all of the + * information within the wallet. It returns a ImportMultisigResponse with the + * derived address. + * @param walletHandle + * @param version + * @param threshold + * @param pks + */ + async importMultisig( + walletHandle: string, + version: number, + threshold: number, + pks: string[] + ) { + const req = { + wallet_handle_token: walletHandle, + multisig_version: version, + threshold, + pks, + }; + const res = await this.c.post('/v1/multisig/import', req); + return res.body; + } + + /** + * exportMultisig accepts a wallet handle, wallet password, and multisig + * address, and returns an ExportMultisigResponse containing the stored + * multisig preimage. The preimage contains all of the information necessary + * to derive the multisig address, including version, threshold, and a list of + * public keys. + * @param walletHandle + * @param walletPassword + * @param addr + */ + async exportMultisig(walletHandle: string, addr: string) { + const req = { + wallet_handle_token: walletHandle, + address: addr, + }; + const res = await this.c.post('/v1/multisig/export', req); + return res.body; + } + + /** + * signMultisigTransaction accepts a wallet handle, wallet password, + * transaction, public key (*not* an address), and an optional partial + * MultisigSig. It looks up the secret key corresponding to the public key, and + * returns a SignMultisigTransactionResponse containing a MultisigSig with a + * signature by the secret key included. + * @param walletHandle + * @param pw + * @param tx + * @param pk + * @param partial + */ + async signMultisigTransaction( + walletHandle: string, + pw: string, + transaction: txn.TransactionLike, + pk: Uint8Array | string, + partial: string + ) { + const tx = txn.instantiateTxnIfNeeded(transaction); + const req = { + wallet_handle_token: walletHandle, + transaction: Buffer.from(tx.toByte()).toString('base64'), + public_key: Buffer.from(pk).toString('base64'), + partial_multisig: partial, + wallet_password: pw, + }; + const res = await this.c.post('/v1/multisig/sign', req); + return res.body; + } + + /** + * deleteMultisig accepts a wallet handle, wallet password, and multisig + * address, and deletes the information about this multisig address from the + * wallet (including address and secret key). + * @param walletHandle + * @param walletPassword + * @param addr + */ + async deleteMultisig( + walletHandle: string, + walletPassword: string, + addr: string + ) { + const req = { + wallet_handle_token: walletHandle, + address: addr, + wallet_password: walletPassword, + }; + const res = await this.c.delete('/v1/multisig', req); + return res.body; + } +} diff --git a/src/client/urlTokenBaseHTTPClient.ts b/src/client/urlTokenBaseHTTPClient.ts new file mode 100644 index 0000000..aba61fe --- /dev/null +++ b/src/client/urlTokenBaseHTTPClient.ts @@ -0,0 +1,218 @@ +import { Buffer } from 'buffer'; +import { fetch, Response, Headers } from 'cross-fetch'; +import { + BaseHTTPClient, + BaseHTTPClientResponse, + BaseHTTPClientError, + Query, +} from './baseHTTPClient'; + +export interface AlgodTokenHeader { + 'X-Algo-API-Token': string; +} + +export interface IndexerTokenHeader { + 'X-Indexer-API-Token': string; +} + +export interface KMDTokenHeader { + 'X-KMD-API-Token': string; +} + +export interface CustomTokenHeader { + [headerName: string]: string; +} + +class URLTokenBaseHTTPError extends Error implements BaseHTTPClientError { + constructor(message: string, public response: BaseHTTPClientResponse) { + super(message); + this.name = 'URLTokenBaseHTTPError'; + this.response = response; + } +} + +export type TokenHeader = + | AlgodTokenHeader + | IndexerTokenHeader + | KMDTokenHeader + | CustomTokenHeader; + +/** + * Implementation of BaseHTTPClient that uses a URL and a token + * and make the REST queries using fetch. + * This is the default implementation of BaseHTTPClient. + */ +export class URLTokenBaseHTTPClient implements BaseHTTPClient { + private readonly baseURL: URL; + private readonly tokenHeader: TokenHeader; + + constructor( + tokenHeader: TokenHeader, + baseServer: string, + port?: string | number, + private defaultHeaders: Record = {} + ) { + // Append a trailing slash so we can use relative paths. Without the trailing + // slash, the last path segment will be replaced by the relative path. See + // usage in `addressWithPath`. + const fixedBaseServer = baseServer.endsWith('/') + ? baseServer + : `${baseServer}/`; + const baseServerURL = new URL(fixedBaseServer); + if (typeof port !== 'undefined') { + baseServerURL.port = port.toString(); + } + + if (baseServerURL.protocol.length === 0) { + throw new Error('Invalid base server URL, protocol must be defined.'); + } + + this.baseURL = baseServerURL; + this.tokenHeader = tokenHeader; + } + + /** + * Compute the URL for a path relative to the instance's address + * @param relativePath - A path string + * @param query - An optional key-value object of query parameters to add to the URL. If the + * relativePath already has query parameters on it, the additional parameters defined here will + * be added to the URL without modifying those (unless a key collision occurs). + * @returns A URL string + */ + private getURL(relativePath: string, query?: Query): string { + let fixedRelativePath: string; + if (relativePath.startsWith('./')) { + fixedRelativePath = relativePath; + } else if (relativePath.startsWith('/')) { + fixedRelativePath = `.${relativePath}`; + } else { + fixedRelativePath = `./${relativePath}`; + } + const address = new URL(fixedRelativePath, this.baseURL); + if (query) { + for (const [key, value] of Object.entries(query)) { + address.searchParams.set(key, value); + } + } + return address.toString(); + } + + private static formatFetchResponseHeaders( + headers: Headers + ): Record { + const headersObj: Record = {}; + headers.forEach((key, value) => { + headersObj[key] = value; + }); + return headersObj; + } + + private static async checkHttpError(res: Response) { + if (res.ok) { + return; + } + + let body: Uint8Array | null = null; + let bodyErrorMessage: string | null = null; + + try { + body = new Uint8Array(await res.arrayBuffer()); + const decoded: Record = JSON.parse( + Buffer.from(body).toString() + ); + if (decoded.message) { + bodyErrorMessage = decoded.message; + } + } catch (_) { + // ignore any error that happened while we are parsing the error response + } + + let message = `Network request error. Received status ${res.status} (${res.statusText})`; + if (bodyErrorMessage) { + message += `: ${bodyErrorMessage}`; + } + + throw new URLTokenBaseHTTPError(message, { + body, + status: res.status, + headers: URLTokenBaseHTTPClient.formatFetchResponseHeaders(res.headers), + }); + } + + private static async formatFetchResponse( + res: Response + ): Promise { + await this.checkHttpError(res); + return { + body: new Uint8Array(await res.arrayBuffer()), + status: res.status, + headers: URLTokenBaseHTTPClient.formatFetchResponseHeaders(res.headers), + }; + } + + async get( + relativePath: string, + query?: Query, + requestHeaders: Record = {} + ): Promise { + // Expand headers for use in fetch + const headers = { + ...this.tokenHeader, + ...this.defaultHeaders, + ...requestHeaders, + }; + + const res = await fetch(this.getURL(relativePath, query), { + mode: 'cors', + headers, + }); + + return URLTokenBaseHTTPClient.formatFetchResponse(res); + } + + async post( + relativePath: string, + data: Uint8Array, + query?: Query, + requestHeaders: Record = {} + ): Promise { + // Expand headers for use in fetch + const headers = { + ...this.tokenHeader, + ...this.defaultHeaders, + ...requestHeaders, + }; + + const res = await fetch(this.getURL(relativePath, query), { + method: 'POST', + mode: 'cors', + body: data, + headers, + }); + + return URLTokenBaseHTTPClient.formatFetchResponse(res); + } + + async delete( + relativePath: string, + data: Uint8Array, + query?: Query, + requestHeaders: Record = {} + ): Promise { + // Expand headers for use in fetch + const headers = { + ...this.tokenHeader, + ...this.defaultHeaders, + ...requestHeaders, + }; + + const res = await fetch(this.getURL(relativePath, query), { + method: 'DELETE', + mode: 'cors', + body: data, + headers, + }); + + return URLTokenBaseHTTPClient.formatFetchResponse(res); + } +} diff --git a/src/client/v2/algod/accountApplicationInformation.ts b/src/client/v2/algod/accountApplicationInformation.ts new file mode 100644 index 0000000..26ec531 --- /dev/null +++ b/src/client/v2/algod/accountApplicationInformation.ts @@ -0,0 +1,20 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class AccountApplicationInformation extends JSONRequest { + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private account: string, + private applicationID: number + ) { + super(c, intDecoding); + this.account = account; + this.applicationID = applicationID; + } + + path() { + return `/v2/accounts/${this.account}/applications/${this.applicationID}`; + } +} diff --git a/src/client/v2/algod/accountAssetInformation.ts b/src/client/v2/algod/accountAssetInformation.ts new file mode 100644 index 0000000..f05e2f8 --- /dev/null +++ b/src/client/v2/algod/accountAssetInformation.ts @@ -0,0 +1,20 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class AccountAssetInformation extends JSONRequest { + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private account: string, + private assetID: number + ) { + super(c, intDecoding); + this.account = account; + this.assetID = assetID; + } + + path() { + return `/v2/accounts/${this.account}/assets/${this.assetID}`; + } +} diff --git a/src/client/v2/algod/accountInformation.ts b/src/client/v2/algod/accountInformation.ts new file mode 100644 index 0000000..bbff871 --- /dev/null +++ b/src/client/v2/algod/accountInformation.ts @@ -0,0 +1,37 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class AccountInformation extends JSONRequest { + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private account: string + ) { + super(c, intDecoding); + this.account = account; + } + + path() { + return `/v2/accounts/${this.account}`; + } + + /** + * Exclude assets and application data from results + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await algodClient.accountInformation(address) + * .exclude('all') + * .do(); + * ``` + * + * @param round + * @category query + */ + exclude(exclude: string) { + this.query.exclude = exclude; + return this; + } +} diff --git a/src/client/v2/algod/algod.ts b/src/client/v2/algod/algod.ts new file mode 100644 index 0000000..8da85a9 --- /dev/null +++ b/src/client/v2/algod/algod.ts @@ -0,0 +1,816 @@ +import ServiceClient from '../serviceClient'; +import * as modelsv2 from './models/types'; +import AccountInformation from './accountInformation'; +import AccountAssetInformation from './accountAssetInformation'; +import AccountApplicationInformation from './accountApplicationInformation'; +import Block from './block'; +import Compile from './compile'; +import Dryrun from './dryrun'; +import Genesis from './genesis'; +import GetAssetByID from './getAssetByID'; +import GetApplicationByID from './getApplicationByID'; +import GetBlockHash from './getBlockHash'; +import GetApplicationBoxByName from './getApplicationBoxByName'; +import GetApplicationBoxes from './getApplicationBoxes'; +import HealthCheck from './healthCheck'; +import PendingTransactionInformation from './pendingTransactionInformation'; +import PendingTransactions from './pendingTransactions'; +import PendingTransactionsByAddress from './pendingTransactionsByAddress'; +import GetTransactionProof from './getTransactionProof'; +import SendRawTransaction from './sendRawTransaction'; +import Status from './status'; +import StatusAfterBlock from './statusAfterBlock'; +import SuggestedParams from './suggestedParams'; +import Supply from './supply'; +import Versions from './versions'; +import { BaseHTTPClient } from '../../baseHTTPClient'; +import { + AlgodTokenHeader, + CustomTokenHeader, +} from '../../urlTokenBaseHTTPClient'; +import LightBlockHeaderProof from './lightBlockHeaderProof'; +import StateProof from './stateproof'; +import SetSyncRound from './setSyncRound'; +import GetSyncRound from './getSyncRound'; +import SetBlockOffsetTimestamp from './setBlockOffsetTimestamp'; +import GetBlockOffsetTimestamp from './getBlockOffsetTimestamp'; +import Disassemble from './disassemble'; +import SimulateRawTransactions from './simulateTransaction'; +import { EncodedSignedTransaction } from '../../../types'; +import * as encoding from '../../../encoding/encoding'; +import Ready from './ready'; +import UnsetSyncRound from './unsetSyncRound'; +import GetLedgerStateDeltaForTransactionGroup from './getLedgerStateDeltaForTransactionGroup'; +import GetLedgerStateDelta from './getLedgerStateDelta'; +import GetTransactionGroupLedgerStateDeltasForRound from './getTransactionGroupLedgerStateDeltasForRound'; + +/** + * Algod client connects an application to the Algorand blockchain. The algod client requires a valid algod REST endpoint IP address and algod token from an Algorand node that is connected to the network you plan to interact with. + * + * Algod is the main Algorand process for handling the blockchain. Messages between nodes are processed, the protocol steps are executed, and the blocks are written to disk. The algod process also exposes a REST API server that developers can use to communicate with the node and the network. Algod uses the data directory for storage and configuration information. + * + * #### Relevant Information + * [How do I obtain an algod address and token?](https://developer.algorand.org/docs/archive/build-apps/setup/?from_query=algod#how-do-i-obtain-an-algod-address-and-token) + * + * [Run Algod in Postman OAS3](https://developer.algorand.org/docs/rest-apis/restendpoints/?from_query=algod#algod-indexer-and-kmd-rest-endpoints) + */ +export default class AlgodClient extends ServiceClient { + /** + * Create an AlgodClient from + * * either a token, baseServer, port, and optional headers + * * or a base client server for interoperability with external dApp wallets + * + * #### Example + * ```typescript + * const token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + * const server = "http://localhost"; + * const port = 4001; + * const algodClient = new algosdk.Algodv2(token, server, port); + * ``` + * @remarks + * The above configuration is for a sandbox private network. + * For applications on production, you are encouraged to run your own node, or use an Algorand REST API provider with a dedicated API key. + * + * @param tokenOrBaseClient - The algod token from the Algorand node you are interacting with + * @param baseServer - REST endpoint + * @param port - Port number if specifically configured by the server + * @param headers - Optional headers + */ + constructor( + tokenOrBaseClient: + | string + | AlgodTokenHeader + | CustomTokenHeader + | BaseHTTPClient, + baseServer: string, + port?: string | number, + headers: Record = {} + ) { + super('X-Algo-API-Token', tokenOrBaseClient, baseServer, port, headers); + } + + /** + * Returns OK if healthy. + * + * #### Example + * ```typescript + * const health = await algodClient.healthCheck().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-health) + * @category GET + */ + healthCheck() { + return new HealthCheck(this.c); + } + + /** + * Retrieves the supported API versions, binary build versions, and genesis information. + * + * #### Example + * ```typescript + * const versionsDetails = await algodClient.versionsCheck().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-versions) + * @category GET + */ + versionsCheck() { + return new Versions(this.c); + } + + /** + * Broadcasts a raw transaction to the network. + * + * #### Example + * ```typescript + * const { txId } = await algodClient.sendRawTransaction(signedTxns).do(); + * const result = await waitForConfirmation(algodClient, txid, 3); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#post-v2transactions) + * + * @remarks + * Often used with {@linkcode waitForConfirmation} + * @param stxOrStxs - Signed transactions + * @category POST + */ + sendRawTransaction(stxOrStxs: Uint8Array | Uint8Array[]) { + return new SendRawTransaction(this.c, stxOrStxs); + } + + /** + * Returns the given account's status, balance and spendable amounts. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await algodClient.accountInformation(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2accountsaddress) + * @param account - The address of the account to look up. + * @category GET + */ + accountInformation(account: string) { + return new AccountInformation(this.c, this.intDecoding, account); + } + + /** + * Returns the given account's asset information for a specific asset. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const index = 60553466; + * const accountAssetInfo = await algodClient.accountAssetInformation(address, index).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2accountsaddress) + * @param account - The address of the account to look up. + * @param index - The asset ID to look up. + * @category GET + */ + accountAssetInformation(account: string, index: number) { + return new AccountAssetInformation( + this.c, + this.intDecoding, + account, + index + ); + } + + /** + * Returns the given account's application information for a specific application. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const index = 60553466; + * const accountInfo = await algodClient.accountApplicationInformation(address, index).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2accountsaddress) + * @param account - The address of the account to look up. + * @param index - The application ID to look up. + * @category GET + */ + accountApplicationInformation(account: string, index: number) { + return new AccountApplicationInformation( + this.c, + this.intDecoding, + account, + index + ); + } + + /** + * Gets the block info for the given round. + * + * #### Example + * ```typescript + * const roundNumber = 18038133; + * const block = await algodClient.block(roundNumber).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2blocksround) + * @param roundNumber - The round number of the block to get. + * @category GET + */ + block(roundNumber: number) { + return new Block(this.c, roundNumber); + } + + /** + * Get the block hash for the block on the given round. + * + * #### Example + * ```typescript + * const roundNumber = 18038133; + * const block = await algodClient.getBlockHash(roundNumber).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2blocksroundhash) + * @param roundNumber - The round number of the block to get. + * @category GET + */ + getBlockHash(roundNumber: number) { + return new GetBlockHash(this.c, this.intDecoding, roundNumber); + } + + /** + * Returns the transaction information for a specific pending transaction. + * + * #### Example + * ```typescript + * const txId = "DRJS6R745A7GFVMXEXWP4TGVDGKW7VILFTA7HC2BR2GRLHNY5CTA"; + * const pending = await algodClient.pendingTransactionInformation(txId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2transactionspendingtxid) + * + * @remarks + *

+ * There are several cases when this might succeed: + * - transaction committed (committed round > 0) + * - transaction still in the pool (committed round = 0, pool error = "") + * - transaction removed from pool due to error (committed round = 0, pool error != "") + * + * Or the transaction may have happened sufficiently long ago that the node no longer remembers it, and this will return an error. + * + * @param txid - The TxID string of the pending transaction to look up. + * @category GET + */ + pendingTransactionInformation(txid: string) { + return new PendingTransactionInformation(this.c, txid); + } + + /** + * Returns the list of pending transactions in the pool, sorted by priority, in decreasing order, truncated at the end at MAX. + * If MAX = 0, returns all pending transactions. + * + * #### Example 1 + * ```typescript + * const pendingTxns = await algodClient.pendingTransactionsInformation().do(); + * ``` + * + * #### Example 2 + * ```typescript + * const maxTxns = 5; + * const pendingTxns = await algodClient + * .pendingTransactionsInformation() + * .max(maxTxns) + * .do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2transactionspending) + * @category GET + */ + pendingTransactionsInformation() { + return new PendingTransactions(this.c); + } + + /** + * Returns the list of pending transactions sent by the address, sorted by priority, in decreasing order, truncated at the end at MAX. + * If MAX = 0, returns all pending transactions. + * + * #### Example 1 + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const pendingTxnsByAddr = await algodClient.pendingTransactionByAddress(address).do(); + * ``` + * + * #### Example 2 + * ```typescript + * const maxTxns = 5; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const pendingTxns = await algodClient + * .pendingTransactionByAddress(address) + * .max(maxTxns) + * .do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2accountsaddresstransactionspending) + * @param address - The address of the sender. + * @category GET + */ + pendingTransactionByAddress(address: string) { + return new PendingTransactionsByAddress(this.c, address); + } + + /** + * Retrieves the StatusResponse from the running node. + * + * #### Example + * ```typescript + * const status = await algodClient.status().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2status) + * @category GET + */ + status() { + return new Status(this.c, this.intDecoding); + } + + /** + * Waits for a specific round to occur then returns the `StatusResponse` for that round. + * + * #### Example + * ```typescript + * const round = 18038133; + * const statusAfterBlock = await algodClient.statusAfterBlock(round).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2statuswait-for-block-afterround) + * @param round - The number of the round to wait for. + * @category GET + */ + statusAfterBlock(round: number) { + return new StatusAfterBlock(this.c, this.intDecoding, round); + } + + /** + * Returns the common needed parameters for a new transaction. + * + * #### Example + * ```typescript + * const suggestedParams = await algodClient.getTransactionParams().do(); + * const amountInMicroAlgos = algosdk.algosToMicroalgos(2); // 2 Algos + * const unsignedTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + * from: senderAddress, + * to: receiverAddress, + * amount: amountInMicroAlgos, + * suggestedParams: suggestedParams, + * }); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2transactionsparams) + * + * @remarks + * Often used with + * {@linkcode makePaymentTxnWithSuggestedParamsFromObject}, {@linkcode algosToMicroalgos} + * @category GET + */ + getTransactionParams() { + return new SuggestedParams(this.c); + } + + /** + * Returns the supply details for the specified node's ledger. + * + * #### Example + * ```typescript + * const supplyDetails = await algodClient.supply().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2ledgersupply) + * @category GET + */ + supply() { + return new Supply(this.c, this.intDecoding); + } + + /** + * Compiles TEAL source code to binary, returns base64 encoded program bytes and base32 SHA512_256 hash of program bytes (Address style). + * + * #### Example + * ```typescript + * const source = "TEAL SOURCE CODE"; + * const compiledSmartContract = await algodClient.compile(source).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#post-v2tealcompile) + * @remarks + * This endpoint is only enabled when a node's configuration file sets `EnableDeveloperAPI` to true. + * @param source + * @category POST + */ + compile(source: string | Uint8Array) { + return new Compile(this.c, source); + } + + /** + * Given the program bytes, return the TEAL source code in plain text. + * + * #### Example + * ```typescript + * const bytecode = "TEAL bytecode"; + * const disassembledSource = await algodClient.disassemble(bytecode).do(); + * ``` + * + * @remarks This endpoint is only enabled when a node's configuration file sets EnableDeveloperAPI to true. + * @param source + */ + disassemble(source: string | Uint8Array) { + return new Disassemble(this.c, source); + } + + /** + * Provides debugging information for a transaction (or group). + * + * Executes TEAL program(s) in context and returns debugging information about the execution. This endpoint is only enabled when a node's configureation file sets `EnableDeveloperAPI` to true. + * + * #### Example + * ```typescript + * const dryRunResult = await algodClient.dryrun(dr).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#post-v2tealdryrun) + * @param dr + * @category POST + */ + dryrun(dr: modelsv2.DryrunRequest) { + return new Dryrun(this.c, dr); + } + + /** + * Given an asset ID, return asset information including creator, name, total supply and + * special addresses. + * + * #### Example + * ```typescript + * const asset_id = 163650; + * const asset = await algodClient.getAssetByID(asset_id).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2assetsasset-id) + * @param index - The asset ID to look up. + * @category GET + */ + getAssetByID(index: number) { + return new GetAssetByID(this.c, this.intDecoding, index); + } + + /** + * Given an application ID, return the application information including creator, approval + * and clear programs, global and local schemas, and global state. + * + * #### Example + * ```typescript + * const index = 60553466; + * const app = await algodClient.getApplicationByID(index).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2applicationsapplication-id) + * @param index - The application ID to look up. + * @category GET + */ + getApplicationByID(index: number) { + return new GetApplicationByID(this.c, this.intDecoding, index); + } + + /** + * Given an application ID and the box name (key), return the value stored in the box. + * + * #### Example + * ```typescript + * const index = 60553466; + * const boxName = Buffer.from("foo"); + * const boxResponse = await algodClient.getApplicationBoxByName(index, boxName).do(); + * const boxValue = boxResponse.value; + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2applicationsapplication-idbox) + * @param index - The application ID to look up. + * @category GET + */ + getApplicationBoxByName(index: number, boxName: Uint8Array) { + return new GetApplicationBoxByName( + this.c, + this.intDecoding, + index, + boxName + ); + } + + /** + * Given an application ID, return all the box names associated with the app. + * + * #### Example + * ```typescript + * const index = 60553466; + * const boxesResponse = await algodClient.getApplicationBoxes(index).max(3).do(); + * const boxNames = boxesResponse.boxes.map(box => box.name); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2applicationsapplication-idboxes) + * @param index - The application ID to look up. + * @category GET + */ + getApplicationBoxes(index: number) { + return new GetApplicationBoxes(this.c, this.intDecoding, index); + } + + /** + * Returns the entire genesis file. + * + * #### Example + * ```typescript + * const genesis = await algodClient.genesis().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-genesis) + * @category GET + */ + genesis() { + return new Genesis(this.c, this.intDecoding); + } + + /** + * Returns a Merkle proof for a given transaction in a block. + * + * #### Example + * ```typescript + * const round = 18038133; + * const txId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const proof = await algodClient.getTransactionProof(round, txId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2blocksroundtransactionstxidproof) + * @param round - The round in which the transaction appears. + * @param txID - The transaction ID for which to generate a proof. + * @category GET + */ + getTransactionProof(round: number, txID: string) { + return new GetTransactionProof(this.c, this.intDecoding, round, txID); + } + + /** + * Gets a proof for a given light block header inside a state proof commitment. + * + * #### Example + * ```typescript + * const round = 11111111; + * const lightBlockHeaderProof = await algodClient.getLightBlockHeaderProof(round).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/v2#get-v2blocksroundlightheaderproof) + * @param round + */ + getLightBlockHeaderProof(round: number) { + return new LightBlockHeaderProof(this.c, this.intDecoding, round); + } + + /** + * Gets a state proof that covers a given round. + * + * #### Example + * ```typescript + * const round = 11111111; + * const stateProof = await algodClient.getStateProof(round).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/v2#get-v2stateproofsround) + * @param round + */ + getStateProof(round: number) { + return new StateProof(this.c, this.intDecoding, round); + } + + /** + * Simulate a list of a signed transaction objects being sent to the network. + * + * #### Example + * ```typescript + * const txn1 = algosdk.makePaymentTxnWithSuggestedParamsFromObject(txn1Params); + * const txn2 = algosdk.makePaymentTxnWithSuggestedParamsFromObject(txn2Params); + * const txgroup = algosdk.assignGroupID([txn1, txn2]); + * + * // Actually sign the first transaction + * const signedTxn1 = txgroup[0].signTxn(senderSk).blob; + * // Simulate does not require signed transactions -- use this method to encode an unsigned transaction + * const signedTxn2 = algosdk.encodeUnsignedSimulateTransaction(txgroup[1]); + * + * const resp = await client.simulateRawTransactions([signedTxn1, signedTxn2]).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#post-v2transactionssimulate) + * @param stxOrStxs + * @category POST + */ + simulateRawTransactions(stxOrStxs: Uint8Array | Uint8Array[]) { + const txnObjects: EncodedSignedTransaction[] = []; + if (Array.isArray(stxOrStxs)) { + for (const stxn of stxOrStxs) { + txnObjects.push(encoding.decode(stxn) as EncodedSignedTransaction); + } + } else { + txnObjects.push(encoding.decode(stxOrStxs) as EncodedSignedTransaction); + } + const request = new modelsv2.SimulateRequest({ + txnGroups: [ + new modelsv2.SimulateRequestTransactionGroup({ + txns: txnObjects, + }), + ], + }); + return this.simulateTransactions(request); + } + + /** + * Simulate transactions being sent to the network. + * + * #### Example + * ```typescript + * const txn1 = algosdk.makePaymentTxnWithSuggestedParamsFromObject(txn1Params); + * const txn2 = algosdk.makePaymentTxnWithSuggestedParamsFromObject(txn2Params); + * const txgroup = algosdk.assignGroupID([txn1, txn2]); + * + * // Actually sign the first transaction + * const signedTxn1 = txgroup[0].signTxn(senderSk).blob; + * // Simulate does not require signed transactions -- use this method to encode an unsigned transaction + * const signedTxn2 = algosdk.encodeUnsignedSimulateTransaction(txgroup[1]); + * + * const request = new modelsv2.SimulateRequest({ + * txnGroups: [ + * new modelsv2.SimulateRequestTransactionGroup({ + * // Must decode the signed txn bytes into an object + * txns: [algosdk.decodeObj(signedTxn1), algosdk.decodeObj(signedTxn2)] + * }), + * ], + * }); + * const resp = await client.simulateRawTransactions(request).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#post-v2transactionssimulate) + * @param request + * @category POST + */ + simulateTransactions(request: modelsv2.SimulateRequest) { + return new SimulateRawTransactions(this.c, request); + } + + /** + * Set the offset (in seconds) applied to the block timestamp when creating new blocks in devmode. + * + * #### Example + * ```typesecript + * const offset = 60 + * await client.setBlockOffsetTimestamp(offset).do(); + * ``` + * + [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#post-v2devmodeblocksoffsetoffset) + * @param offset + * @category POST + */ + setBlockOffsetTimestamp(offset: number) { + return new SetBlockOffsetTimestamp(this.c, this.intDecoding, offset); + } + + /** + * Get the offset (in seconds) applied to the block timestamp when creating new blocks in devmode. + * + * #### Example + * ```typesecript + * const currentOffset = await client.getBlockOffsetTimestamp().do(); + * ``` + * + [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2devmodeblocksoffset) + * @category GET + */ + getBlockOffsetTimestamp() { + return new GetBlockOffsetTimestamp(this.c, this.intDecoding); + } + + /** + * Set the sync round on the ledger (algod must have EnableFollowMode: true), restricting catchup. + * + * #### Example + * ```typesecript + * const round = 10000 + * await client.setSyncRound(round).do(); + * ``` + * + [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#post-v2ledgersyncround) + * @param round + * @category POST + */ + setSyncRound(round: number) { + return new SetSyncRound(this.c, this.intDecoding, round); + } + + /** + * Un-Set the sync round on the ledger (algod must have EnableFollowMode: true), removing the restriction on catchup. + * + * #### Example + * ```typesecript + * await client.unsetSyncRound().do(); + * ``` + * + [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#delete-v2ledgersync) + * @category DELETE + */ + unsetSyncRound() { + return new UnsetSyncRound(this.c, this.intDecoding); + } + + /** + * Get the current sync round on the ledger (algod must have EnableFollowMode: true). + * + * #### Example + * ```typesecript + * const currentSyncRound = await client.getSyncRound().do(); + * ``` + * + [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2ledgersync) + * @category GET + */ + getSyncRound() { + return new GetSyncRound(this.c, this.intDecoding); + } + + /** + * Ready check which returns 200 OK if algod is healthy and caught up + * + * #### Example + * ```typesecript + * await client.ready().do(); + * ``` + * + [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-ready) + * @category GET + */ + ready() { + return new Ready(this.c, this.intDecoding); + } + + /** + * GetLedgerStateDeltaForTransactionGroup returns the ledger delta for the txn group identified by id + * + * #### Example + * ```typescript + * const id = "ABC123"; + * await client.getLedgerStateDeltaForTransactionGroup(id).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2deltastxngroupid) + * @param id txn ID or group ID to be searched for + * @category GET + */ + getLedgerStateDeltaForTransactionGroup(id: string) { + return new GetLedgerStateDeltaForTransactionGroup( + this.c, + this.intDecoding, + id + ); + } + + /** + * GetLedgerStateDelta returns the ledger delta for the entire round + * + * #### Example + * ```typescript + * const round = 12345; + * await client.getLedgerStateDelta(round).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2deltasround) + * @param round the round number to be searched for + * @category GET + */ + getLedgerStateDelta(round: bigint) { + return new GetLedgerStateDelta(this.c, this.intDecoding, round); + } + + /** + * GetTransactionGroupLedgerStateDeltasForRound returns all ledger deltas for txn groups in the provided round + * + * #### Example + * ```typescript + * const round = 12345; + * await client.getTransactionGroupLedgerStateDeltasForRound(round).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2deltasroundtxngroup) + * @param round the round number to be searched for + * @category GET + */ + getTransactionGroupLedgerStateDeltasForRound(round: bigint) { + return new GetTransactionGroupLedgerStateDeltasForRound( + this.c, + this.intDecoding, + round + ); + } +} diff --git a/src/client/v2/algod/block.ts b/src/client/v2/algod/block.ts new file mode 100644 index 0000000..24dc2e2 --- /dev/null +++ b/src/client/v2/algod/block.ts @@ -0,0 +1,30 @@ +import * as encoding from '../../../encoding/encoding'; +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; + +/** + * block gets the block info for the given round. this call may block + */ +export default class Block extends JSONRequest { + private round: number; + + constructor(c: HTTPClient, roundNumber: number) { + super(c); + if (!Number.isInteger(roundNumber)) + throw Error('roundNumber should be an integer'); + this.round = roundNumber; + this.query = { format: 'msgpack' }; + } + + path() { + return `/v2/blocks/${this.round}`; + } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Uint8Array) { + if (body && body.byteLength > 0) { + return encoding.decode(body) as Record; + } + return undefined; + } +} diff --git a/src/client/v2/algod/compile.ts b/src/client/v2/algod/compile.ts new file mode 100644 index 0000000..db8769c --- /dev/null +++ b/src/client/v2/algod/compile.ts @@ -0,0 +1,51 @@ +import { Buffer } from 'buffer'; +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; + +/** + * Sets the default header (if not previously set) + * @param headers - A headers object + */ +export function setHeaders(headers = {}) { + let hdrs = headers; + if (Object.keys(hdrs).every((key) => key.toLowerCase() !== 'content-type')) { + hdrs = { ...headers }; + hdrs['Content-Type'] = 'text/plain'; + } + return hdrs; +} + +/** + * Executes compile + */ +export default class Compile extends JSONRequest { + constructor(c: HTTPClient, private source: string | Uint8Array) { + super(c); + this.source = source; + } + + // eslint-disable-next-line class-methods-use-this + path() { + return `/v2/teal/compile`; + } + + sourcemap(map: boolean = true) { + this.query.sourcemap = map; + return this; + } + + /** + * Executes compile + * @param headers - A headers object + */ + async do(headers = {}) { + const txHeaders = setHeaders(headers); + const res = await this.c.post( + this.path(), + Buffer.from(this.source), + txHeaders, + this.query + ); + return res.body; + } +} diff --git a/src/client/v2/algod/disassemble.ts b/src/client/v2/algod/disassemble.ts new file mode 100644 index 0000000..552e8c6 --- /dev/null +++ b/src/client/v2/algod/disassemble.ts @@ -0,0 +1,46 @@ +import { Buffer } from 'buffer'; +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; + +/** + * Sets the default header (if not previously set) + * @param headers - A headers object + */ +export function setHeaders(headers = {}) { + let hdrs = headers; + if (Object.keys(hdrs).every((key) => key.toLowerCase() !== 'content-type')) { + hdrs = { ...headers }; + hdrs['Content-Type'] = 'text/plain'; + } + return hdrs; +} + +/** + * Executes disassemble + */ +export default class Disassemble extends JSONRequest { + constructor(c: HTTPClient, private source: string | Uint8Array) { + super(c); + this.source = source; + } + + // eslint-disable-next-line class-methods-use-this + path() { + return `/v2/teal/disassemble`; + } + + /** + * Executes disassemble + * @param headers - A headers object + */ + async do(headers = {}) { + const txHeaders = setHeaders(headers); + const res = await this.c.post( + this.path(), + Buffer.from(this.source), + txHeaders, + this.query + ); + return res.body; + } +} diff --git a/src/client/v2/algod/dryrun.ts b/src/client/v2/algod/dryrun.ts new file mode 100644 index 0000000..98b2fb4 --- /dev/null +++ b/src/client/v2/algod/dryrun.ts @@ -0,0 +1,34 @@ +import { Buffer } from 'buffer'; +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import * as modelsv2 from './models/types'; +import * as encoding from '../../../encoding/encoding'; +import { setHeaders } from './compile'; + +export default class Dryrun extends JSONRequest { + private blob: Uint8Array; + + constructor(c: HTTPClient, dr: modelsv2.DryrunRequest) { + super(c); + this.blob = encoding.encode(dr.get_obj_for_encoding(true)); + } + + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/teal/dryrun'; + } + + /** + * Executes dryrun + * @param headers - A headers object + */ + async do(headers = {}) { + const txHeaders = setHeaders(headers); + const res = await this.c.post( + this.path(), + Buffer.from(this.blob), + txHeaders + ); + return res.body; + } +} diff --git a/src/client/v2/algod/genesis.ts b/src/client/v2/algod/genesis.ts new file mode 100644 index 0000000..2f88cad --- /dev/null +++ b/src/client/v2/algod/genesis.ts @@ -0,0 +1,8 @@ +import JSONRequest from '../jsonrequest'; + +export default class Genesis extends JSONRequest { + // eslint-disable-next-line class-methods-use-this + path() { + return '/genesis'; + } +} diff --git a/src/client/v2/algod/getApplicationBoxByName.ts b/src/client/v2/algod/getApplicationBoxByName.ts new file mode 100644 index 0000000..b58aaf8 --- /dev/null +++ b/src/client/v2/algod/getApplicationBoxByName.ts @@ -0,0 +1,50 @@ +import { Buffer } from 'buffer'; +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; +import { Box } from './models/types'; + +/** + * Given an application ID and the box name (key), return the value stored in the box. + * + * #### Example + * ```typescript + * const index = 60553466; + * const boxName = Buffer.from("foo"); + * const boxResponse = await algodClient.getApplicationBoxByName(index, boxName).do(); + * const boxValue = boxResponse.value; + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2applicationsapplication-idbox) + * @param index - The application ID to look up. + * @category GET + */ +export default class GetApplicationBoxByName extends JSONRequest< + Box, + Record +> { + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private index: number, + name: Uint8Array + ) { + super(c, intDecoding); + this.index = index; + // Encode name in base64 format and append the encoding prefix. + const encodedName = Buffer.from(name).toString('base64'); + this.query.name = encodeURI(`b64:${encodedName}`); + } + + /** + * @returns `/v2/applications/${index}/box` + */ + path() { + return `/v2/applications/${this.index}/box`; + } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): Box { + return Box.from_obj_for_encoding(body); + } +} diff --git a/src/client/v2/algod/getApplicationBoxes.ts b/src/client/v2/algod/getApplicationBoxes.ts new file mode 100644 index 0000000..68f6f04 --- /dev/null +++ b/src/client/v2/algod/getApplicationBoxes.ts @@ -0,0 +1,61 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; +import { BoxesResponse } from './models/types'; + +/** + * Given an application ID, return all the box names associated with the app. + * + * #### Example + * ```typescript + * const index = 60553466; + * const boxesResponse = await algodClient.getApplicationBoxes(index).max(3).do(); + * const boxNames = boxesResponse.boxes.map(box => box.name); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-v2applicationsapplication-idboxes) + * @param index - The application ID to look up. + * @category GET + */ +export default class GetApplicationBoxes extends JSONRequest< + BoxesResponse, + Record +> { + constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { + super(c, intDecoding); + this.index = index; + this.query.max = 0; + } + + /** + * @returns `/v2/applications/${index}/boxes` + */ + path() { + return `/v2/applications/${this.index}/boxes`; + } + + /** + * Limit results for pagination. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const boxesResult = await algodClient + * .GetApplicationBoxes(1234) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + max(max: number) { + this.query.max = max; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): BoxesResponse { + return BoxesResponse.from_obj_for_encoding(body); + } +} diff --git a/src/client/v2/algod/getApplicationByID.ts b/src/client/v2/algod/getApplicationByID.ts new file mode 100644 index 0000000..91e1c12 --- /dev/null +++ b/src/client/v2/algod/getApplicationByID.ts @@ -0,0 +1,14 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class GetApplicationByID extends JSONRequest { + constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { + super(c, intDecoding); + this.index = index; + } + + path() { + return `/v2/applications/${this.index}`; + } +} diff --git a/src/client/v2/algod/getAssetByID.ts b/src/client/v2/algod/getAssetByID.ts new file mode 100644 index 0000000..3abb569 --- /dev/null +++ b/src/client/v2/algod/getAssetByID.ts @@ -0,0 +1,14 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class GetAssetByID extends JSONRequest { + constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { + super(c, intDecoding); + this.index = index; + } + + path() { + return `/v2/assets/${this.index}`; + } +} diff --git a/src/client/v2/algod/getBlockHash.ts b/src/client/v2/algod/getBlockHash.ts new file mode 100644 index 0000000..40d01b4 --- /dev/null +++ b/src/client/v2/algod/getBlockHash.ts @@ -0,0 +1,18 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class GetBlockHash extends JSONRequest { + round: number; + + constructor(c: HTTPClient, intDecoding: IntDecoding, roundNumber: number) { + super(c, intDecoding); + if (!Number.isInteger(roundNumber)) + throw Error('roundNumber should be an integer'); + this.round = roundNumber; + } + + path() { + return `/v2/blocks/${this.round}/hash`; + } +} diff --git a/src/client/v2/algod/getBlockOffsetTimestamp.ts b/src/client/v2/algod/getBlockOffsetTimestamp.ts new file mode 100644 index 0000000..a2c04fe --- /dev/null +++ b/src/client/v2/algod/getBlockOffsetTimestamp.ts @@ -0,0 +1,17 @@ +import JSONRequest from '../jsonrequest'; +import { GetBlockTimeStampOffsetResponse } from './models/types'; + +export default class GetBlockOffsetTimestamp extends JSONRequest< + GetBlockTimeStampOffsetResponse, + Record +> { + // eslint-disable-next-line class-methods-use-this + path() { + return `/v2/devmode/blocks/offset`; + } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): GetBlockTimeStampOffsetResponse { + return GetBlockTimeStampOffsetResponse.from_obj_for_encoding(body); + } +} diff --git a/src/client/v2/algod/getLedgerStateDelta.ts b/src/client/v2/algod/getLedgerStateDelta.ts new file mode 100644 index 0000000..ac69dae --- /dev/null +++ b/src/client/v2/algod/getLedgerStateDelta.ts @@ -0,0 +1,16 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class GetLedgerStateDelta extends JSONRequest { + constructor(c: HTTPClient, intDecoding: IntDecoding, private round: bigint) { + super(c, intDecoding); + this.round = round; + this.query = { format: 'json' }; + } + + // eslint-disable-next-line class-methods-use-this + path() { + return `/v2/deltas/${this.round}`; + } +} diff --git a/src/client/v2/algod/getLedgerStateDeltaForTransactionGroup.ts b/src/client/v2/algod/getLedgerStateDeltaForTransactionGroup.ts new file mode 100644 index 0000000..4186232 --- /dev/null +++ b/src/client/v2/algod/getLedgerStateDeltaForTransactionGroup.ts @@ -0,0 +1,16 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class GetLedgerStateDeltaForTransactionGroup extends JSONRequest { + constructor(c: HTTPClient, intDecoding: IntDecoding, private id: string) { + super(c, intDecoding); + this.id = id; + this.query = { format: 'json' }; + } + + // eslint-disable-next-line class-methods-use-this + path() { + return `/v2/deltas/txn/group/${this.id}`; + } +} diff --git a/src/client/v2/algod/getSyncRound.ts b/src/client/v2/algod/getSyncRound.ts new file mode 100644 index 0000000..4a761a1 --- /dev/null +++ b/src/client/v2/algod/getSyncRound.ts @@ -0,0 +1,17 @@ +import JSONRequest from '../jsonrequest'; +import { GetSyncRoundResponse } from './models/types'; + +export default class GetSyncRound extends JSONRequest< + GetSyncRoundResponse, + Record +> { + // eslint-disable-next-line class-methods-use-this + path() { + return `/v2/ledger/sync`; + } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): GetSyncRoundResponse { + return GetSyncRoundResponse.from_obj_for_encoding(body); + } +} diff --git a/src/client/v2/algod/getTransactionGroupLedgerStateDeltasForRound.ts b/src/client/v2/algod/getTransactionGroupLedgerStateDeltasForRound.ts new file mode 100644 index 0000000..1c17760 --- /dev/null +++ b/src/client/v2/algod/getTransactionGroupLedgerStateDeltasForRound.ts @@ -0,0 +1,29 @@ +import JSONRequest from '../jsonrequest'; +import { TransactionGroupLedgerStateDeltasForRoundResponse } from './models/types'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class GetTransactionGroupLedgerStateDeltasForRound extends JSONRequest< + TransactionGroupLedgerStateDeltasForRoundResponse, + Record +> { + constructor(c: HTTPClient, intDecoding: IntDecoding, private round: bigint) { + super(c, intDecoding); + this.round = round; + this.query = { format: 'json' }; + } + + // eslint-disable-next-line class-methods-use-this + path() { + return `/v2/deltas/${this.round}/txn/group`; + } + + // eslint-disable-next-line class-methods-use-this + prepare( + body: Record + ): TransactionGroupLedgerStateDeltasForRoundResponse { + return TransactionGroupLedgerStateDeltasForRoundResponse.from_obj_for_encoding( + body + ); + } +} diff --git a/src/client/v2/algod/getTransactionProof.ts b/src/client/v2/algod/getTransactionProof.ts new file mode 100644 index 0000000..398039a --- /dev/null +++ b/src/client/v2/algod/getTransactionProof.ts @@ -0,0 +1,43 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class GetTransactionProof extends JSONRequest { + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private round: number, + private txID: string + ) { + super(c, intDecoding); + + this.round = round; + this.txID = txID; + } + + path() { + return `/v2/blocks/${this.round}/transactions/${this.txID}/proof`; + } + + /** + * Exclude assets and application data from results + * The type of hash function used to create the proof, must be one of: "sha512_256", "sha256" + * + * #### Example + * ```typescript + * const hashType = "sha256"; + * const round = 123456; + * const txId = "abc123; + * const txProof = await algodClient.getTransactionProof(round, txId) + * .hashType(hashType) + * .do(); + * ``` + * + * @param hashType + * @category query + */ + hashType(hashType: string) { + this.query.hashtype = hashType; + return this; + } +} diff --git a/src/client/v2/algod/healthCheck.ts b/src/client/v2/algod/healthCheck.ts new file mode 100644 index 0000000..e6077f0 --- /dev/null +++ b/src/client/v2/algod/healthCheck.ts @@ -0,0 +1,19 @@ +import JSONRequest from '../jsonrequest'; + +/** + * healthCheck returns an empty object iff the node is running + */ +export default class HealthCheck extends JSONRequest { + // eslint-disable-next-line class-methods-use-this + path() { + return '/health'; + } + + async do(headers = {}) { + const res = await this.c.get(this.path(), {}, headers); + if (!res.ok) { + throw new Error(`Health response: ${res.status}`); + } + return {}; + } +} diff --git a/src/client/v2/algod/lightBlockHeaderProof.ts b/src/client/v2/algod/lightBlockHeaderProof.ts new file mode 100644 index 0000000..ac3794c --- /dev/null +++ b/src/client/v2/algod/lightBlockHeaderProof.ts @@ -0,0 +1,15 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LightBlockHeaderProof extends JSONRequest { + constructor(c: HTTPClient, intDecoding: IntDecoding, private round: number) { + super(c, intDecoding); + + this.round = round; + } + + path() { + return `/v2/blocks/${this.round}/lightheader/proof`; + } +} diff --git a/src/client/v2/algod/models/types.ts b/src/client/v2/algod/models/types.ts new file mode 100644 index 0000000..ace6b61 --- /dev/null +++ b/src/client/v2/algod/models/types.ts @@ -0,0 +1,4478 @@ +/** + * NOTICE: This file was generated. Editing this file manually is not recommended. + */ + +/* eslint-disable no-use-before-define */ +import { Buffer } from 'buffer'; +import BaseModel from '../../basemodel'; +import { EncodedSignedTransaction } from '../../../../types/transactions/encoded'; +import BlockHeader from '../../../../types/blockHeader'; + +/** + * Account information at a given round. + * Definition: + * data/basics/userBalance.go : AccountData + */ +export class Account extends BaseModel { + /** + * the account public key + */ + public address: string; + + /** + * (algo) total number of MicroAlgos in the account + */ + public amount: number | bigint; + + /** + * specifies the amount of MicroAlgos in the account, without the pending rewards. + */ + public amountWithoutPendingRewards: number | bigint; + + /** + * MicroAlgo balance required by the account. + * The requirement grows based on asset and application usage. + */ + public minBalance: number | bigint; + + /** + * amount of MicroAlgos of pending rewards in this account. + */ + public pendingRewards: number | bigint; + + /** + * (ern) total rewards of MicroAlgos the account has received, including pending + * rewards. + */ + public rewards: number | bigint; + + /** + * The round for which this information is relevant. + */ + public round: number | bigint; + + /** + * (onl) delegation status of the account's MicroAlgos + * * Offline - indicates that the associated account is delegated. + * * Online - indicates that the associated account used as part of the delegation + * pool. + * * NotParticipating - indicates that the associated account is neither a + * delegator nor a delegate. + */ + public status: string; + + /** + * The count of all applications that have been opted in, equivalent to the count + * of application local data (AppLocalState objects) stored in this account. + */ + public totalAppsOptedIn: number | bigint; + + /** + * The count of all assets that have been opted in, equivalent to the count of + * AssetHolding objects held by this account. + */ + public totalAssetsOptedIn: number | bigint; + + /** + * The count of all apps (AppParams objects) created by this account. + */ + public totalCreatedApps: number | bigint; + + /** + * The count of all assets (AssetParams objects) created by this account. + */ + public totalCreatedAssets: number | bigint; + + /** + * (appl) applications local data stored in this account. + * Note the raw object uses `map[int] -> AppLocalState` for this type. + */ + public appsLocalState?: ApplicationLocalState[]; + + /** + * (teap) the sum of all extra application program pages for this account. + */ + public appsTotalExtraPages?: number | bigint; + + /** + * (tsch) stores the sum of all of the local schemas and global schemas in this + * account. + * Note: the raw account uses `StateSchema` for this type. + */ + public appsTotalSchema?: ApplicationStateSchema; + + /** + * (asset) assets held by this account. + * Note the raw object uses `map[int] -> AssetHolding` for this type. + */ + public assets?: AssetHolding[]; + + /** + * (spend) the address against which signing should be checked. If empty, the + * address of the current account is used. This field can be updated in any + * transaction by setting the RekeyTo field. + */ + public authAddr?: string; + + /** + * (appp) parameters of applications created by this account including app global + * data. + * Note: the raw account uses `map[int] -> AppParams` for this type. + */ + public createdApps?: Application[]; + + /** + * (apar) parameters of assets created by this account. + * Note: the raw account uses `map[int] -> Asset` for this type. + */ + public createdAssets?: Asset[]; + + /** + * AccountParticipation describes the parameters used by this account in consensus + * protocol. + */ + public participation?: AccountParticipation; + + /** + * (ebase) used as part of the rewards computation. Only applicable to accounts + * which are participating. + */ + public rewardBase?: number | bigint; + + /** + * Indicates what type of signature is used by this account, must be one of: + * * sig + * * msig + * * lsig + */ + public sigType?: string; + + /** + * (tbxb) The total number of bytes used by this account's app's box keys and + * values. + */ + public totalBoxBytes?: number | bigint; + + /** + * (tbx) The number of existing boxes created by this account's app. + */ + public totalBoxes?: number | bigint; + + /** + * Creates a new `Account` object. + * @param address - the account public key + * @param amount - (algo) total number of MicroAlgos in the account + * @param amountWithoutPendingRewards - specifies the amount of MicroAlgos in the account, without the pending rewards. + * @param minBalance - MicroAlgo balance required by the account. + * The requirement grows based on asset and application usage. + * @param pendingRewards - amount of MicroAlgos of pending rewards in this account. + * @param rewards - (ern) total rewards of MicroAlgos the account has received, including pending + * rewards. + * @param round - The round for which this information is relevant. + * @param status - (onl) delegation status of the account's MicroAlgos + * * Offline - indicates that the associated account is delegated. + * * Online - indicates that the associated account used as part of the delegation + * pool. + * * NotParticipating - indicates that the associated account is neither a + * delegator nor a delegate. + * @param totalAppsOptedIn - The count of all applications that have been opted in, equivalent to the count + * of application local data (AppLocalState objects) stored in this account. + * @param totalAssetsOptedIn - The count of all assets that have been opted in, equivalent to the count of + * AssetHolding objects held by this account. + * @param totalCreatedApps - The count of all apps (AppParams objects) created by this account. + * @param totalCreatedAssets - The count of all assets (AssetParams objects) created by this account. + * @param appsLocalState - (appl) applications local data stored in this account. + * Note the raw object uses `map[int] -> AppLocalState` for this type. + * @param appsTotalExtraPages - (teap) the sum of all extra application program pages for this account. + * @param appsTotalSchema - (tsch) stores the sum of all of the local schemas and global schemas in this + * account. + * Note: the raw account uses `StateSchema` for this type. + * @param assets - (asset) assets held by this account. + * Note the raw object uses `map[int] -> AssetHolding` for this type. + * @param authAddr - (spend) the address against which signing should be checked. If empty, the + * address of the current account is used. This field can be updated in any + * transaction by setting the RekeyTo field. + * @param createdApps - (appp) parameters of applications created by this account including app global + * data. + * Note: the raw account uses `map[int] -> AppParams` for this type. + * @param createdAssets - (apar) parameters of assets created by this account. + * Note: the raw account uses `map[int] -> Asset` for this type. + * @param participation - AccountParticipation describes the parameters used by this account in consensus + * protocol. + * @param rewardBase - (ebase) used as part of the rewards computation. Only applicable to accounts + * which are participating. + * @param sigType - Indicates what type of signature is used by this account, must be one of: + * * sig + * * msig + * * lsig + * @param totalBoxBytes - (tbxb) The total number of bytes used by this account's app's box keys and + * values. + * @param totalBoxes - (tbx) The number of existing boxes created by this account's app. + */ + constructor({ + address, + amount, + amountWithoutPendingRewards, + minBalance, + pendingRewards, + rewards, + round, + status, + totalAppsOptedIn, + totalAssetsOptedIn, + totalCreatedApps, + totalCreatedAssets, + appsLocalState, + appsTotalExtraPages, + appsTotalSchema, + assets, + authAddr, + createdApps, + createdAssets, + participation, + rewardBase, + sigType, + totalBoxBytes, + totalBoxes, + }: { + address: string; + amount: number | bigint; + amountWithoutPendingRewards: number | bigint; + minBalance: number | bigint; + pendingRewards: number | bigint; + rewards: number | bigint; + round: number | bigint; + status: string; + totalAppsOptedIn: number | bigint; + totalAssetsOptedIn: number | bigint; + totalCreatedApps: number | bigint; + totalCreatedAssets: number | bigint; + appsLocalState?: ApplicationLocalState[]; + appsTotalExtraPages?: number | bigint; + appsTotalSchema?: ApplicationStateSchema; + assets?: AssetHolding[]; + authAddr?: string; + createdApps?: Application[]; + createdAssets?: Asset[]; + participation?: AccountParticipation; + rewardBase?: number | bigint; + sigType?: string; + totalBoxBytes?: number | bigint; + totalBoxes?: number | bigint; + }) { + super(); + this.address = address; + this.amount = amount; + this.amountWithoutPendingRewards = amountWithoutPendingRewards; + this.minBalance = minBalance; + this.pendingRewards = pendingRewards; + this.rewards = rewards; + this.round = round; + this.status = status; + this.totalAppsOptedIn = totalAppsOptedIn; + this.totalAssetsOptedIn = totalAssetsOptedIn; + this.totalCreatedApps = totalCreatedApps; + this.totalCreatedAssets = totalCreatedAssets; + this.appsLocalState = appsLocalState; + this.appsTotalExtraPages = appsTotalExtraPages; + this.appsTotalSchema = appsTotalSchema; + this.assets = assets; + this.authAddr = authAddr; + this.createdApps = createdApps; + this.createdAssets = createdAssets; + this.participation = participation; + this.rewardBase = rewardBase; + this.sigType = sigType; + this.totalBoxBytes = totalBoxBytes; + this.totalBoxes = totalBoxes; + + this.attribute_map = { + address: 'address', + amount: 'amount', + amountWithoutPendingRewards: 'amount-without-pending-rewards', + minBalance: 'min-balance', + pendingRewards: 'pending-rewards', + rewards: 'rewards', + round: 'round', + status: 'status', + totalAppsOptedIn: 'total-apps-opted-in', + totalAssetsOptedIn: 'total-assets-opted-in', + totalCreatedApps: 'total-created-apps', + totalCreatedAssets: 'total-created-assets', + appsLocalState: 'apps-local-state', + appsTotalExtraPages: 'apps-total-extra-pages', + appsTotalSchema: 'apps-total-schema', + assets: 'assets', + authAddr: 'auth-addr', + createdApps: 'created-apps', + createdAssets: 'created-assets', + participation: 'participation', + rewardBase: 'reward-base', + sigType: 'sig-type', + totalBoxBytes: 'total-box-bytes', + totalBoxes: 'total-boxes', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): Account { + /* eslint-disable dot-notation */ + if (typeof data['address'] === 'undefined') + throw new Error(`Response is missing required field 'address': ${data}`); + if (typeof data['amount'] === 'undefined') + throw new Error(`Response is missing required field 'amount': ${data}`); + if (typeof data['amount-without-pending-rewards'] === 'undefined') + throw new Error( + `Response is missing required field 'amount-without-pending-rewards': ${data}` + ); + if (typeof data['min-balance'] === 'undefined') + throw new Error( + `Response is missing required field 'min-balance': ${data}` + ); + if (typeof data['pending-rewards'] === 'undefined') + throw new Error( + `Response is missing required field 'pending-rewards': ${data}` + ); + if (typeof data['rewards'] === 'undefined') + throw new Error(`Response is missing required field 'rewards': ${data}`); + if (typeof data['round'] === 'undefined') + throw new Error(`Response is missing required field 'round': ${data}`); + if (typeof data['status'] === 'undefined') + throw new Error(`Response is missing required field 'status': ${data}`); + if (typeof data['total-apps-opted-in'] === 'undefined') + throw new Error( + `Response is missing required field 'total-apps-opted-in': ${data}` + ); + if (typeof data['total-assets-opted-in'] === 'undefined') + throw new Error( + `Response is missing required field 'total-assets-opted-in': ${data}` + ); + if (typeof data['total-created-apps'] === 'undefined') + throw new Error( + `Response is missing required field 'total-created-apps': ${data}` + ); + if (typeof data['total-created-assets'] === 'undefined') + throw new Error( + `Response is missing required field 'total-created-assets': ${data}` + ); + return new Account({ + address: data['address'], + amount: data['amount'], + amountWithoutPendingRewards: data['amount-without-pending-rewards'], + minBalance: data['min-balance'], + pendingRewards: data['pending-rewards'], + rewards: data['rewards'], + round: data['round'], + status: data['status'], + totalAppsOptedIn: data['total-apps-opted-in'], + totalAssetsOptedIn: data['total-assets-opted-in'], + totalCreatedApps: data['total-created-apps'], + totalCreatedAssets: data['total-created-assets'], + appsLocalState: + typeof data['apps-local-state'] !== 'undefined' + ? data['apps-local-state'].map( + ApplicationLocalState.from_obj_for_encoding + ) + : undefined, + appsTotalExtraPages: data['apps-total-extra-pages'], + appsTotalSchema: + typeof data['apps-total-schema'] !== 'undefined' + ? ApplicationStateSchema.from_obj_for_encoding( + data['apps-total-schema'] + ) + : undefined, + assets: + typeof data['assets'] !== 'undefined' + ? data['assets'].map(AssetHolding.from_obj_for_encoding) + : undefined, + authAddr: data['auth-addr'], + createdApps: + typeof data['created-apps'] !== 'undefined' + ? data['created-apps'].map(Application.from_obj_for_encoding) + : undefined, + createdAssets: + typeof data['created-assets'] !== 'undefined' + ? data['created-assets'].map(Asset.from_obj_for_encoding) + : undefined, + participation: + typeof data['participation'] !== 'undefined' + ? AccountParticipation.from_obj_for_encoding(data['participation']) + : undefined, + rewardBase: data['reward-base'], + sigType: data['sig-type'], + totalBoxBytes: data['total-box-bytes'], + totalBoxes: data['total-boxes'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * AccountApplicationResponse describes the account's application local state and + * global state (AppLocalState and AppParams, if either exists) for a specific + * application ID. Global state will only be returned if the provided address is + * the application's creator. + */ +export class AccountApplicationResponse extends BaseModel { + /** + * The round for which this information is relevant. + */ + public round: number | bigint; + + /** + * (appl) the application local data stored in this account. + * The raw account uses `AppLocalState` for this type. + */ + public appLocalState?: ApplicationLocalState; + + /** + * (appp) parameters of the application created by this account including app + * global data. + * The raw account uses `AppParams` for this type. + */ + public createdApp?: ApplicationParams; + + /** + * Creates a new `AccountApplicationResponse` object. + * @param round - The round for which this information is relevant. + * @param appLocalState - (appl) the application local data stored in this account. + * The raw account uses `AppLocalState` for this type. + * @param createdApp - (appp) parameters of the application created by this account including app + * global data. + * The raw account uses `AppParams` for this type. + */ + constructor({ + round, + appLocalState, + createdApp, + }: { + round: number | bigint; + appLocalState?: ApplicationLocalState; + createdApp?: ApplicationParams; + }) { + super(); + this.round = round; + this.appLocalState = appLocalState; + this.createdApp = createdApp; + + this.attribute_map = { + round: 'round', + appLocalState: 'app-local-state', + createdApp: 'created-app', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): AccountApplicationResponse { + /* eslint-disable dot-notation */ + if (typeof data['round'] === 'undefined') + throw new Error(`Response is missing required field 'round': ${data}`); + return new AccountApplicationResponse({ + round: data['round'], + appLocalState: + typeof data['app-local-state'] !== 'undefined' + ? ApplicationLocalState.from_obj_for_encoding(data['app-local-state']) + : undefined, + createdApp: + typeof data['created-app'] !== 'undefined' + ? ApplicationParams.from_obj_for_encoding(data['created-app']) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +/** + * AccountAssetResponse describes the account's asset holding and asset parameters + * (if either exist) for a specific asset ID. Asset parameters will only be + * returned if the provided address is the asset's creator. + */ +export class AccountAssetResponse extends BaseModel { + /** + * The round for which this information is relevant. + */ + public round: number | bigint; + + /** + * (asset) Details about the asset held by this account. + * The raw account uses `AssetHolding` for this type. + */ + public assetHolding?: AssetHolding; + + /** + * (apar) parameters of the asset created by this account. + * The raw account uses `AssetParams` for this type. + */ + public createdAsset?: AssetParams; + + /** + * Creates a new `AccountAssetResponse` object. + * @param round - The round for which this information is relevant. + * @param assetHolding - (asset) Details about the asset held by this account. + * The raw account uses `AssetHolding` for this type. + * @param createdAsset - (apar) parameters of the asset created by this account. + * The raw account uses `AssetParams` for this type. + */ + constructor({ + round, + assetHolding, + createdAsset, + }: { + round: number | bigint; + assetHolding?: AssetHolding; + createdAsset?: AssetParams; + }) { + super(); + this.round = round; + this.assetHolding = assetHolding; + this.createdAsset = createdAsset; + + this.attribute_map = { + round: 'round', + assetHolding: 'asset-holding', + createdAsset: 'created-asset', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): AccountAssetResponse { + /* eslint-disable dot-notation */ + if (typeof data['round'] === 'undefined') + throw new Error(`Response is missing required field 'round': ${data}`); + return new AccountAssetResponse({ + round: data['round'], + assetHolding: + typeof data['asset-holding'] !== 'undefined' + ? AssetHolding.from_obj_for_encoding(data['asset-holding']) + : undefined, + createdAsset: + typeof data['created-asset'] !== 'undefined' + ? AssetParams.from_obj_for_encoding(data['created-asset']) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +/** + * AccountParticipation describes the parameters used by this account in consensus + * protocol. + */ +export class AccountParticipation extends BaseModel { + /** + * (sel) Selection public key (if any) currently registered for this round. + */ + public selectionParticipationKey: Uint8Array; + + /** + * (voteFst) First round for which this participation is valid. + */ + public voteFirstValid: number | bigint; + + /** + * (voteKD) Number of subkeys in each batch of participation keys. + */ + public voteKeyDilution: number | bigint; + + /** + * (voteLst) Last round for which this participation is valid. + */ + public voteLastValid: number | bigint; + + /** + * (vote) root participation public key (if any) currently registered for this + * round. + */ + public voteParticipationKey: Uint8Array; + + /** + * (stprf) Root of the state proof key (if any) + */ + public stateProofKey?: Uint8Array; + + /** + * Creates a new `AccountParticipation` object. + * @param selectionParticipationKey - (sel) Selection public key (if any) currently registered for this round. + * @param voteFirstValid - (voteFst) First round for which this participation is valid. + * @param voteKeyDilution - (voteKD) Number of subkeys in each batch of participation keys. + * @param voteLastValid - (voteLst) Last round for which this participation is valid. + * @param voteParticipationKey - (vote) root participation public key (if any) currently registered for this + * round. + * @param stateProofKey - (stprf) Root of the state proof key (if any) + */ + constructor({ + selectionParticipationKey, + voteFirstValid, + voteKeyDilution, + voteLastValid, + voteParticipationKey, + stateProofKey, + }: { + selectionParticipationKey: string | Uint8Array; + voteFirstValid: number | bigint; + voteKeyDilution: number | bigint; + voteLastValid: number | bigint; + voteParticipationKey: string | Uint8Array; + stateProofKey?: string | Uint8Array; + }) { + super(); + this.selectionParticipationKey = + typeof selectionParticipationKey === 'string' + ? new Uint8Array(Buffer.from(selectionParticipationKey, 'base64')) + : selectionParticipationKey; + this.voteFirstValid = voteFirstValid; + this.voteKeyDilution = voteKeyDilution; + this.voteLastValid = voteLastValid; + this.voteParticipationKey = + typeof voteParticipationKey === 'string' + ? new Uint8Array(Buffer.from(voteParticipationKey, 'base64')) + : voteParticipationKey; + this.stateProofKey = + typeof stateProofKey === 'string' + ? new Uint8Array(Buffer.from(stateProofKey, 'base64')) + : stateProofKey; + + this.attribute_map = { + selectionParticipationKey: 'selection-participation-key', + voteFirstValid: 'vote-first-valid', + voteKeyDilution: 'vote-key-dilution', + voteLastValid: 'vote-last-valid', + voteParticipationKey: 'vote-participation-key', + stateProofKey: 'state-proof-key', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): AccountParticipation { + /* eslint-disable dot-notation */ + if (typeof data['selection-participation-key'] === 'undefined') + throw new Error( + `Response is missing required field 'selection-participation-key': ${data}` + ); + if (typeof data['vote-first-valid'] === 'undefined') + throw new Error( + `Response is missing required field 'vote-first-valid': ${data}` + ); + if (typeof data['vote-key-dilution'] === 'undefined') + throw new Error( + `Response is missing required field 'vote-key-dilution': ${data}` + ); + if (typeof data['vote-last-valid'] === 'undefined') + throw new Error( + `Response is missing required field 'vote-last-valid': ${data}` + ); + if (typeof data['vote-participation-key'] === 'undefined') + throw new Error( + `Response is missing required field 'vote-participation-key': ${data}` + ); + return new AccountParticipation({ + selectionParticipationKey: data['selection-participation-key'], + voteFirstValid: data['vote-first-valid'], + voteKeyDilution: data['vote-key-dilution'], + voteLastValid: data['vote-last-valid'], + voteParticipationKey: data['vote-participation-key'], + stateProofKey: data['state-proof-key'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Application state delta. + */ +export class AccountStateDelta extends BaseModel { + public address: string; + + /** + * Application state delta. + */ + public delta: EvalDeltaKeyValue[]; + + /** + * Creates a new `AccountStateDelta` object. + * @param address - + * @param delta - Application state delta. + */ + constructor({ + address, + delta, + }: { + address: string; + delta: EvalDeltaKeyValue[]; + }) { + super(); + this.address = address; + this.delta = delta; + + this.attribute_map = { + address: 'address', + delta: 'delta', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): AccountStateDelta { + /* eslint-disable dot-notation */ + if (typeof data['address'] === 'undefined') + throw new Error(`Response is missing required field 'address': ${data}`); + if (!Array.isArray(data['delta'])) + throw new Error( + `Response is missing required array field 'delta': ${data}` + ); + return new AccountStateDelta({ + address: data['address'], + delta: data['delta'].map(EvalDeltaKeyValue.from_obj_for_encoding), + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Application index and its parameters + */ +export class Application extends BaseModel { + /** + * (appidx) application index. + */ + public id: number | bigint; + + /** + * (appparams) application parameters. + */ + public params: ApplicationParams; + + /** + * Creates a new `Application` object. + * @param id - (appidx) application index. + * @param params - (appparams) application parameters. + */ + constructor({ + id, + params, + }: { + id: number | bigint; + params: ApplicationParams; + }) { + super(); + this.id = id; + this.params = params; + + this.attribute_map = { + id: 'id', + params: 'params', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): Application { + /* eslint-disable dot-notation */ + if (typeof data['id'] === 'undefined') + throw new Error(`Response is missing required field 'id': ${data}`); + if (typeof data['params'] === 'undefined') + throw new Error(`Response is missing required field 'params': ${data}`); + return new Application({ + id: data['id'], + params: ApplicationParams.from_obj_for_encoding(data['params']), + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Stores local state associated with an application. + */ +export class ApplicationLocalState extends BaseModel { + /** + * The application which this local state is for. + */ + public id: number | bigint; + + /** + * (hsch) schema. + */ + public schema: ApplicationStateSchema; + + /** + * (tkv) storage. + */ + public keyValue?: TealKeyValue[]; + + /** + * Creates a new `ApplicationLocalState` object. + * @param id - The application which this local state is for. + * @param schema - (hsch) schema. + * @param keyValue - (tkv) storage. + */ + constructor({ + id, + schema, + keyValue, + }: { + id: number | bigint; + schema: ApplicationStateSchema; + keyValue?: TealKeyValue[]; + }) { + super(); + this.id = id; + this.schema = schema; + this.keyValue = keyValue; + + this.attribute_map = { + id: 'id', + schema: 'schema', + keyValue: 'key-value', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): ApplicationLocalState { + /* eslint-disable dot-notation */ + if (typeof data['id'] === 'undefined') + throw new Error(`Response is missing required field 'id': ${data}`); + if (typeof data['schema'] === 'undefined') + throw new Error(`Response is missing required field 'schema': ${data}`); + return new ApplicationLocalState({ + id: data['id'], + schema: ApplicationStateSchema.from_obj_for_encoding(data['schema']), + keyValue: + typeof data['key-value'] !== 'undefined' + ? data['key-value'].map(TealKeyValue.from_obj_for_encoding) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Stores the global information associated with an application. + */ +export class ApplicationParams extends BaseModel { + /** + * (approv) approval program. + */ + public approvalProgram: Uint8Array; + + /** + * (clearp) approval program. + */ + public clearStateProgram: Uint8Array; + + /** + * The address that created this application. This is the address where the + * parameters and global state for this application can be found. + */ + public creator: string; + + /** + * (epp) the amount of extra program pages available to this app. + */ + public extraProgramPages?: number | bigint; + + /** + * (gs) global state + */ + public globalState?: TealKeyValue[]; + + /** + * (gsch) global schema + */ + public globalStateSchema?: ApplicationStateSchema; + + /** + * (lsch) local schema + */ + public localStateSchema?: ApplicationStateSchema; + + /** + * Creates a new `ApplicationParams` object. + * @param approvalProgram - (approv) approval program. + * @param clearStateProgram - (clearp) approval program. + * @param creator - The address that created this application. This is the address where the + * parameters and global state for this application can be found. + * @param extraProgramPages - (epp) the amount of extra program pages available to this app. + * @param globalState - (gs) global state + * @param globalStateSchema - (gsch) global schema + * @param localStateSchema - (lsch) local schema + */ + constructor({ + approvalProgram, + clearStateProgram, + creator, + extraProgramPages, + globalState, + globalStateSchema, + localStateSchema, + }: { + approvalProgram: string | Uint8Array; + clearStateProgram: string | Uint8Array; + creator: string; + extraProgramPages?: number | bigint; + globalState?: TealKeyValue[]; + globalStateSchema?: ApplicationStateSchema; + localStateSchema?: ApplicationStateSchema; + }) { + super(); + this.approvalProgram = + typeof approvalProgram === 'string' + ? new Uint8Array(Buffer.from(approvalProgram, 'base64')) + : approvalProgram; + this.clearStateProgram = + typeof clearStateProgram === 'string' + ? new Uint8Array(Buffer.from(clearStateProgram, 'base64')) + : clearStateProgram; + this.creator = creator; + this.extraProgramPages = extraProgramPages; + this.globalState = globalState; + this.globalStateSchema = globalStateSchema; + this.localStateSchema = localStateSchema; + + this.attribute_map = { + approvalProgram: 'approval-program', + clearStateProgram: 'clear-state-program', + creator: 'creator', + extraProgramPages: 'extra-program-pages', + globalState: 'global-state', + globalStateSchema: 'global-state-schema', + localStateSchema: 'local-state-schema', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): ApplicationParams { + /* eslint-disable dot-notation */ + if (typeof data['approval-program'] === 'undefined') + throw new Error( + `Response is missing required field 'approval-program': ${data}` + ); + if (typeof data['clear-state-program'] === 'undefined') + throw new Error( + `Response is missing required field 'clear-state-program': ${data}` + ); + if (typeof data['creator'] === 'undefined') + throw new Error(`Response is missing required field 'creator': ${data}`); + return new ApplicationParams({ + approvalProgram: data['approval-program'], + clearStateProgram: data['clear-state-program'], + creator: data['creator'], + extraProgramPages: data['extra-program-pages'], + globalState: + typeof data['global-state'] !== 'undefined' + ? data['global-state'].map(TealKeyValue.from_obj_for_encoding) + : undefined, + globalStateSchema: + typeof data['global-state-schema'] !== 'undefined' + ? ApplicationStateSchema.from_obj_for_encoding( + data['global-state-schema'] + ) + : undefined, + localStateSchema: + typeof data['local-state-schema'] !== 'undefined' + ? ApplicationStateSchema.from_obj_for_encoding( + data['local-state-schema'] + ) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Specifies maximums on the number of each type that may be stored. + */ +export class ApplicationStateSchema extends BaseModel { + /** + * (nui) num of uints. + */ + public numUint: number | bigint; + + /** + * (nbs) num of byte slices. + */ + public numByteSlice: number | bigint; + + /** + * Creates a new `ApplicationStateSchema` object. + * @param numUint - (nui) num of uints. + * @param numByteSlice - (nbs) num of byte slices. + */ + constructor({ + numUint, + numByteSlice, + }: { + numUint: number | bigint; + numByteSlice: number | bigint; + }) { + super(); + this.numUint = numUint; + this.numByteSlice = numByteSlice; + + this.attribute_map = { + numUint: 'num-uint', + numByteSlice: 'num-byte-slice', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): ApplicationStateSchema { + /* eslint-disable dot-notation */ + if (typeof data['num-uint'] === 'undefined') + throw new Error(`Response is missing required field 'num-uint': ${data}`); + if (typeof data['num-byte-slice'] === 'undefined') + throw new Error( + `Response is missing required field 'num-byte-slice': ${data}` + ); + return new ApplicationStateSchema({ + numUint: data['num-uint'], + numByteSlice: data['num-byte-slice'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Specifies both the unique identifier and the parameters for an asset + */ +export class Asset extends BaseModel { + /** + * unique asset identifier + */ + public index: number | bigint; + + /** + * AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + */ + public params: AssetParams; + + /** + * Creates a new `Asset` object. + * @param index - unique asset identifier + * @param params - AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + */ + constructor({ + index, + params, + }: { + index: number | bigint; + params: AssetParams; + }) { + super(); + this.index = index; + this.params = params; + + this.attribute_map = { + index: 'index', + params: 'params', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): Asset { + /* eslint-disable dot-notation */ + if (typeof data['index'] === 'undefined') + throw new Error(`Response is missing required field 'index': ${data}`); + if (typeof data['params'] === 'undefined') + throw new Error(`Response is missing required field 'params': ${data}`); + return new Asset({ + index: data['index'], + params: AssetParams.from_obj_for_encoding(data['params']), + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Describes an asset held by an account. + * Definition: + * data/basics/userBalance.go : AssetHolding + */ +export class AssetHolding extends BaseModel { + /** + * (a) number of units held. + */ + public amount: number | bigint; + + /** + * Asset ID of the holding. + */ + public assetId: number | bigint; + + /** + * (f) whether or not the holding is frozen. + */ + public isFrozen: boolean; + + /** + * Creates a new `AssetHolding` object. + * @param amount - (a) number of units held. + * @param assetId - Asset ID of the holding. + * @param isFrozen - (f) whether or not the holding is frozen. + */ + constructor({ + amount, + assetId, + isFrozen, + }: { + amount: number | bigint; + assetId: number | bigint; + isFrozen: boolean; + }) { + super(); + this.amount = amount; + this.assetId = assetId; + this.isFrozen = isFrozen; + + this.attribute_map = { + amount: 'amount', + assetId: 'asset-id', + isFrozen: 'is-frozen', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): AssetHolding { + /* eslint-disable dot-notation */ + if (typeof data['amount'] === 'undefined') + throw new Error(`Response is missing required field 'amount': ${data}`); + if (typeof data['asset-id'] === 'undefined') + throw new Error(`Response is missing required field 'asset-id': ${data}`); + if (typeof data['is-frozen'] === 'undefined') + throw new Error( + `Response is missing required field 'is-frozen': ${data}` + ); + return new AssetHolding({ + amount: data['amount'], + assetId: data['asset-id'], + isFrozen: data['is-frozen'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + */ +export class AssetParams extends BaseModel { + /** + * The address that created this asset. This is the address where the parameters + * for this asset can be found, and also the address where unwanted asset units can + * be sent in the worst case. + */ + public creator: string; + + /** + * (dc) The number of digits to use after the decimal point when displaying this + * asset. If 0, the asset is not divisible. If 1, the base unit of the asset is in + * tenths. If 2, the base unit of the asset is in hundredths, and so on. This value + * must be between 0 and 19 (inclusive). + */ + public decimals: number | bigint; + + /** + * (t) The total number of units of this asset. + */ + public total: number | bigint; + + /** + * (c) Address of account used to clawback holdings of this asset. If empty, + * clawback is not permitted. + */ + public clawback?: string; + + /** + * (df) Whether holdings of this asset are frozen by default. + */ + public defaultFrozen?: boolean; + + /** + * (f) Address of account used to freeze holdings of this asset. If empty, freezing + * is not permitted. + */ + public freeze?: string; + + /** + * (m) Address of account used to manage the keys of this asset and to destroy it. + */ + public manager?: string; + + /** + * (am) A commitment to some unspecified asset metadata. The format of this + * metadata is up to the application. + */ + public metadataHash?: Uint8Array; + + /** + * (an) Name of this asset, as supplied by the creator. Included only when the + * asset name is composed of printable utf-8 characters. + */ + public name?: string; + + /** + * Base64 encoded name of this asset, as supplied by the creator. + */ + public nameB64?: Uint8Array; + + /** + * (r) Address of account holding reserve (non-minted) units of this asset. + */ + public reserve?: string; + + /** + * (un) Name of a unit of this asset, as supplied by the creator. Included only + * when the name of a unit of this asset is composed of printable utf-8 characters. + */ + public unitName?: string; + + /** + * Base64 encoded name of a unit of this asset, as supplied by the creator. + */ + public unitNameB64?: Uint8Array; + + /** + * (au) URL where more information about the asset can be retrieved. Included only + * when the URL is composed of printable utf-8 characters. + */ + public url?: string; + + /** + * Base64 encoded URL where more information about the asset can be retrieved. + */ + public urlB64?: Uint8Array; + + /** + * Creates a new `AssetParams` object. + * @param creator - The address that created this asset. This is the address where the parameters + * for this asset can be found, and also the address where unwanted asset units can + * be sent in the worst case. + * @param decimals - (dc) The number of digits to use after the decimal point when displaying this + * asset. If 0, the asset is not divisible. If 1, the base unit of the asset is in + * tenths. If 2, the base unit of the asset is in hundredths, and so on. This value + * must be between 0 and 19 (inclusive). + * @param total - (t) The total number of units of this asset. + * @param clawback - (c) Address of account used to clawback holdings of this asset. If empty, + * clawback is not permitted. + * @param defaultFrozen - (df) Whether holdings of this asset are frozen by default. + * @param freeze - (f) Address of account used to freeze holdings of this asset. If empty, freezing + * is not permitted. + * @param manager - (m) Address of account used to manage the keys of this asset and to destroy it. + * @param metadataHash - (am) A commitment to some unspecified asset metadata. The format of this + * metadata is up to the application. + * @param name - (an) Name of this asset, as supplied by the creator. Included only when the + * asset name is composed of printable utf-8 characters. + * @param nameB64 - Base64 encoded name of this asset, as supplied by the creator. + * @param reserve - (r) Address of account holding reserve (non-minted) units of this asset. + * @param unitName - (un) Name of a unit of this asset, as supplied by the creator. Included only + * when the name of a unit of this asset is composed of printable utf-8 characters. + * @param unitNameB64 - Base64 encoded name of a unit of this asset, as supplied by the creator. + * @param url - (au) URL where more information about the asset can be retrieved. Included only + * when the URL is composed of printable utf-8 characters. + * @param urlB64 - Base64 encoded URL where more information about the asset can be retrieved. + */ + constructor({ + creator, + decimals, + total, + clawback, + defaultFrozen, + freeze, + manager, + metadataHash, + name, + nameB64, + reserve, + unitName, + unitNameB64, + url, + urlB64, + }: { + creator: string; + decimals: number | bigint; + total: number | bigint; + clawback?: string; + defaultFrozen?: boolean; + freeze?: string; + manager?: string; + metadataHash?: string | Uint8Array; + name?: string; + nameB64?: string | Uint8Array; + reserve?: string; + unitName?: string; + unitNameB64?: string | Uint8Array; + url?: string; + urlB64?: string | Uint8Array; + }) { + super(); + this.creator = creator; + this.decimals = decimals; + this.total = total; + this.clawback = clawback; + this.defaultFrozen = defaultFrozen; + this.freeze = freeze; + this.manager = manager; + this.metadataHash = + typeof metadataHash === 'string' + ? new Uint8Array(Buffer.from(metadataHash, 'base64')) + : metadataHash; + this.name = name; + this.nameB64 = + typeof nameB64 === 'string' + ? new Uint8Array(Buffer.from(nameB64, 'base64')) + : nameB64; + this.reserve = reserve; + this.unitName = unitName; + this.unitNameB64 = + typeof unitNameB64 === 'string' + ? new Uint8Array(Buffer.from(unitNameB64, 'base64')) + : unitNameB64; + this.url = url; + this.urlB64 = + typeof urlB64 === 'string' + ? new Uint8Array(Buffer.from(urlB64, 'base64')) + : urlB64; + + this.attribute_map = { + creator: 'creator', + decimals: 'decimals', + total: 'total', + clawback: 'clawback', + defaultFrozen: 'default-frozen', + freeze: 'freeze', + manager: 'manager', + metadataHash: 'metadata-hash', + name: 'name', + nameB64: 'name-b64', + reserve: 'reserve', + unitName: 'unit-name', + unitNameB64: 'unit-name-b64', + url: 'url', + urlB64: 'url-b64', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): AssetParams { + /* eslint-disable dot-notation */ + if (typeof data['creator'] === 'undefined') + throw new Error(`Response is missing required field 'creator': ${data}`); + if (typeof data['decimals'] === 'undefined') + throw new Error(`Response is missing required field 'decimals': ${data}`); + if (typeof data['total'] === 'undefined') + throw new Error(`Response is missing required field 'total': ${data}`); + return new AssetParams({ + creator: data['creator'], + decimals: data['decimals'], + total: data['total'], + clawback: data['clawback'], + defaultFrozen: data['default-frozen'], + freeze: data['freeze'], + manager: data['manager'], + metadataHash: data['metadata-hash'], + name: data['name'], + nameB64: data['name-b64'], + reserve: data['reserve'], + unitName: data['unit-name'], + unitNameB64: data['unit-name-b64'], + url: data['url'], + urlB64: data['url-b64'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Hash of a block header. + */ +export class BlockHashResponse extends BaseModel { + /** + * Block header hash. + */ + public blockhash: string; + + /** + * Creates a new `BlockHashResponse` object. + * @param blockhash - Block header hash. + */ + constructor({ blockhash }: { blockhash: string }) { + super(); + this.blockhash = blockhash; + + this.attribute_map = { + blockhash: 'blockHash', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): BlockHashResponse { + /* eslint-disable dot-notation */ + if (typeof data['blockHash'] === 'undefined') + throw new Error( + `Response is missing required field 'blockHash': ${data}` + ); + return new BlockHashResponse({ + blockhash: data['blockHash'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Encoded block object. + */ +export class BlockResponse extends BaseModel { + /** + * Block header data. + */ + public block: BlockHeader; + + /** + * Optional certificate object. This is only included when the format is set to + * message pack. + */ + public cert?: Record; + + /** + * Creates a new `BlockResponse` object. + * @param block - Block header data. + * @param cert - Optional certificate object. This is only included when the format is set to + * message pack. + */ + constructor({ + block, + cert, + }: { + block: BlockHeader; + cert?: Record; + }) { + super(); + this.block = block; + this.cert = cert; + + this.attribute_map = { + block: 'block', + cert: 'cert', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): BlockResponse { + /* eslint-disable dot-notation */ + if (typeof data['block'] === 'undefined') + throw new Error(`Response is missing required field 'block': ${data}`); + return new BlockResponse({ + block: data['block'], + cert: data['cert'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Box name and its content. + */ +export class Box extends BaseModel { + /** + * (name) box name, base64 encoded + */ + public name: Uint8Array; + + /** + * (value) box value, base64 encoded. + */ + public value: Uint8Array; + + /** + * Creates a new `Box` object. + * @param name - (name) box name, base64 encoded + * @param value - (value) box value, base64 encoded. + */ + constructor({ + name, + value, + }: { + name: string | Uint8Array; + value: string | Uint8Array; + }) { + super(); + this.name = + typeof name === 'string' + ? new Uint8Array(Buffer.from(name, 'base64')) + : name; + this.value = + typeof value === 'string' + ? new Uint8Array(Buffer.from(value, 'base64')) + : value; + + this.attribute_map = { + name: 'name', + value: 'value', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): Box { + /* eslint-disable dot-notation */ + if (typeof data['name'] === 'undefined') + throw new Error(`Response is missing required field 'name': ${data}`); + if (typeof data['value'] === 'undefined') + throw new Error(`Response is missing required field 'value': ${data}`); + return new Box({ + name: data['name'], + value: data['value'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Box descriptor describes a Box. + */ +export class BoxDescriptor extends BaseModel { + /** + * Base64 encoded box name + */ + public name: Uint8Array; + + /** + * Creates a new `BoxDescriptor` object. + * @param name - Base64 encoded box name + */ + constructor({ name }: { name: string | Uint8Array }) { + super(); + this.name = + typeof name === 'string' + ? new Uint8Array(Buffer.from(name, 'base64')) + : name; + + this.attribute_map = { + name: 'name', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): BoxDescriptor { + /* eslint-disable dot-notation */ + if (typeof data['name'] === 'undefined') + throw new Error(`Response is missing required field 'name': ${data}`); + return new BoxDescriptor({ + name: data['name'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Box names of an application + */ +export class BoxesResponse extends BaseModel { + public boxes: BoxDescriptor[]; + + /** + * Creates a new `BoxesResponse` object. + * @param boxes - + */ + constructor({ boxes }: { boxes: BoxDescriptor[] }) { + super(); + this.boxes = boxes; + + this.attribute_map = { + boxes: 'boxes', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): BoxesResponse { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['boxes'])) + throw new Error( + `Response is missing required array field 'boxes': ${data}` + ); + return new BoxesResponse({ + boxes: data['boxes'].map(BoxDescriptor.from_obj_for_encoding), + }); + /* eslint-enable dot-notation */ + } +} + +export class BuildVersion extends BaseModel { + public branch: string; + + public buildNumber: number | bigint; + + public channel: string; + + public commitHash: string; + + public major: number | bigint; + + public minor: number | bigint; + + /** + * Creates a new `BuildVersion` object. + * @param branch - + * @param buildNumber - + * @param channel - + * @param commitHash - + * @param major - + * @param minor - + */ + constructor({ + branch, + buildNumber, + channel, + commitHash, + major, + minor, + }: { + branch: string; + buildNumber: number | bigint; + channel: string; + commitHash: string; + major: number | bigint; + minor: number | bigint; + }) { + super(); + this.branch = branch; + this.buildNumber = buildNumber; + this.channel = channel; + this.commitHash = commitHash; + this.major = major; + this.minor = minor; + + this.attribute_map = { + branch: 'branch', + buildNumber: 'build_number', + channel: 'channel', + commitHash: 'commit_hash', + major: 'major', + minor: 'minor', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): BuildVersion { + /* eslint-disable dot-notation */ + if (typeof data['branch'] === 'undefined') + throw new Error(`Response is missing required field 'branch': ${data}`); + if (typeof data['build_number'] === 'undefined') + throw new Error( + `Response is missing required field 'build_number': ${data}` + ); + if (typeof data['channel'] === 'undefined') + throw new Error(`Response is missing required field 'channel': ${data}`); + if (typeof data['commit_hash'] === 'undefined') + throw new Error( + `Response is missing required field 'commit_hash': ${data}` + ); + if (typeof data['major'] === 'undefined') + throw new Error(`Response is missing required field 'major': ${data}`); + if (typeof data['minor'] === 'undefined') + throw new Error(`Response is missing required field 'minor': ${data}`); + return new BuildVersion({ + branch: data['branch'], + buildNumber: data['build_number'], + channel: data['channel'], + commitHash: data['commit_hash'], + major: data['major'], + minor: data['minor'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Teal compile Result + */ +export class CompileResponse extends BaseModel { + /** + * base32 SHA512_256 of program bytes (Address style) + */ + public hash: string; + + /** + * base64 encoded program bytes + */ + public result: string; + + /** + * JSON of the source map + */ + public sourcemap?: Record; + + /** + * Creates a new `CompileResponse` object. + * @param hash - base32 SHA512_256 of program bytes (Address style) + * @param result - base64 encoded program bytes + * @param sourcemap - JSON of the source map + */ + constructor({ + hash, + result, + sourcemap, + }: { + hash: string; + result: string; + sourcemap?: Record; + }) { + super(); + this.hash = hash; + this.result = result; + this.sourcemap = sourcemap; + + this.attribute_map = { + hash: 'hash', + result: 'result', + sourcemap: 'sourcemap', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): CompileResponse { + /* eslint-disable dot-notation */ + if (typeof data['hash'] === 'undefined') + throw new Error(`Response is missing required field 'hash': ${data}`); + if (typeof data['result'] === 'undefined') + throw new Error(`Response is missing required field 'result': ${data}`); + return new CompileResponse({ + hash: data['hash'], + result: data['result'], + sourcemap: data['sourcemap'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Teal disassembly Result + */ +export class DisassembleResponse extends BaseModel { + /** + * disassembled Teal code + */ + public result: string; + + /** + * Creates a new `DisassembleResponse` object. + * @param result - disassembled Teal code + */ + constructor({ result }: { result: string }) { + super(); + this.result = result; + + this.attribute_map = { + result: 'result', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): DisassembleResponse { + /* eslint-disable dot-notation */ + if (typeof data['result'] === 'undefined') + throw new Error(`Response is missing required field 'result': ${data}`); + return new DisassembleResponse({ + result: data['result'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Request data type for dryrun endpoint. Given the Transactions and simulated + * ledger state upload, run TEAL scripts and return debugging information. + */ +export class DryrunRequest extends BaseModel { + public accounts: Account[]; + + public apps: Application[]; + + /** + * LatestTimestamp is available to some TEAL scripts. Defaults to the latest + * confirmed timestamp this algod is attached to. + */ + public latestTimestamp: number | bigint; + + /** + * ProtocolVersion specifies a specific version string to operate under, otherwise + * whatever the current protocol of the network this algod is running in. + */ + public protocolVersion: string; + + /** + * Round is available to some TEAL scripts. Defaults to the current round on the + * network this algod is attached to. + */ + public round: number | bigint; + + public sources: DryrunSource[]; + + public txns: EncodedSignedTransaction[]; + + /** + * Creates a new `DryrunRequest` object. + * @param accounts - + * @param apps - + * @param latestTimestamp - LatestTimestamp is available to some TEAL scripts. Defaults to the latest + * confirmed timestamp this algod is attached to. + * @param protocolVersion - ProtocolVersion specifies a specific version string to operate under, otherwise + * whatever the current protocol of the network this algod is running in. + * @param round - Round is available to some TEAL scripts. Defaults to the current round on the + * network this algod is attached to. + * @param sources - + * @param txns - + */ + constructor({ + accounts, + apps, + latestTimestamp, + protocolVersion, + round, + sources, + txns, + }: { + accounts: Account[]; + apps: Application[]; + latestTimestamp: number | bigint; + protocolVersion: string; + round: number | bigint; + sources: DryrunSource[]; + txns: EncodedSignedTransaction[]; + }) { + super(); + this.accounts = accounts; + this.apps = apps; + this.latestTimestamp = latestTimestamp; + this.protocolVersion = protocolVersion; + this.round = round; + this.sources = sources; + this.txns = txns; + + this.attribute_map = { + accounts: 'accounts', + apps: 'apps', + latestTimestamp: 'latest-timestamp', + protocolVersion: 'protocol-version', + round: 'round', + sources: 'sources', + txns: 'txns', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): DryrunRequest { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['accounts'])) + throw new Error( + `Response is missing required array field 'accounts': ${data}` + ); + if (!Array.isArray(data['apps'])) + throw new Error( + `Response is missing required array field 'apps': ${data}` + ); + if (typeof data['latest-timestamp'] === 'undefined') + throw new Error( + `Response is missing required field 'latest-timestamp': ${data}` + ); + if (typeof data['protocol-version'] === 'undefined') + throw new Error( + `Response is missing required field 'protocol-version': ${data}` + ); + if (typeof data['round'] === 'undefined') + throw new Error(`Response is missing required field 'round': ${data}`); + if (!Array.isArray(data['sources'])) + throw new Error( + `Response is missing required array field 'sources': ${data}` + ); + if (!Array.isArray(data['txns'])) + throw new Error( + `Response is missing required array field 'txns': ${data}` + ); + return new DryrunRequest({ + accounts: data['accounts'].map(Account.from_obj_for_encoding), + apps: data['apps'].map(Application.from_obj_for_encoding), + latestTimestamp: data['latest-timestamp'], + protocolVersion: data['protocol-version'], + round: data['round'], + sources: data['sources'].map(DryrunSource.from_obj_for_encoding), + txns: data['txns'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * DryrunResponse contains per-txn debug information from a dryrun. + */ +export class DryrunResponse extends BaseModel { + public error: string; + + /** + * Protocol version is the protocol version Dryrun was operated under. + */ + public protocolVersion: string; + + public txns: DryrunTxnResult[]; + + /** + * Creates a new `DryrunResponse` object. + * @param error - + * @param protocolVersion - Protocol version is the protocol version Dryrun was operated under. + * @param txns - + */ + constructor({ + error, + protocolVersion, + txns, + }: { + error: string; + protocolVersion: string; + txns: DryrunTxnResult[]; + }) { + super(); + this.error = error; + this.protocolVersion = protocolVersion; + this.txns = txns; + + this.attribute_map = { + error: 'error', + protocolVersion: 'protocol-version', + txns: 'txns', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): DryrunResponse { + /* eslint-disable dot-notation */ + if (typeof data['error'] === 'undefined') + throw new Error(`Response is missing required field 'error': ${data}`); + if (typeof data['protocol-version'] === 'undefined') + throw new Error( + `Response is missing required field 'protocol-version': ${data}` + ); + if (!Array.isArray(data['txns'])) + throw new Error( + `Response is missing required array field 'txns': ${data}` + ); + return new DryrunResponse({ + error: data['error'], + protocolVersion: data['protocol-version'], + txns: data['txns'].map(DryrunTxnResult.from_obj_for_encoding), + }); + /* eslint-enable dot-notation */ + } +} + +/** + * DryrunSource is TEAL source text that gets uploaded, compiled, and inserted into + * transactions or application state. + */ +export class DryrunSource extends BaseModel { + /** + * FieldName is what kind of sources this is. If lsig then it goes into the + * transactions[this.TxnIndex].LogicSig. If approv or clearp it goes into the + * Approval Program or Clear State Program of application[this.AppIndex]. + */ + public fieldName: string; + + public source: string; + + public txnIndex: number | bigint; + + public appIndex: number | bigint; + + /** + * Creates a new `DryrunSource` object. + * @param fieldName - FieldName is what kind of sources this is. If lsig then it goes into the + * transactions[this.TxnIndex].LogicSig. If approv or clearp it goes into the + * Approval Program or Clear State Program of application[this.AppIndex]. + * @param source - + * @param txnIndex - + * @param appIndex - + */ + constructor({ + fieldName, + source, + txnIndex, + appIndex, + }: { + fieldName: string; + source: string; + txnIndex: number | bigint; + appIndex: number | bigint; + }) { + super(); + this.fieldName = fieldName; + this.source = source; + this.txnIndex = txnIndex; + this.appIndex = appIndex; + + this.attribute_map = { + fieldName: 'field-name', + source: 'source', + txnIndex: 'txn-index', + appIndex: 'app-index', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): DryrunSource { + /* eslint-disable dot-notation */ + if (typeof data['field-name'] === 'undefined') + throw new Error( + `Response is missing required field 'field-name': ${data}` + ); + if (typeof data['source'] === 'undefined') + throw new Error(`Response is missing required field 'source': ${data}`); + if (typeof data['txn-index'] === 'undefined') + throw new Error( + `Response is missing required field 'txn-index': ${data}` + ); + if (typeof data['app-index'] === 'undefined') + throw new Error( + `Response is missing required field 'app-index': ${data}` + ); + return new DryrunSource({ + fieldName: data['field-name'], + source: data['source'], + txnIndex: data['txn-index'], + appIndex: data['app-index'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Stores the TEAL eval step data + */ +export class DryrunState extends BaseModel { + /** + * Line number + */ + public line: number | bigint; + + /** + * Program counter + */ + public pc: number | bigint; + + public stack: TealValue[]; + + /** + * Evaluation error if any + */ + public error?: string; + + public scratch?: TealValue[]; + + /** + * Creates a new `DryrunState` object. + * @param line - Line number + * @param pc - Program counter + * @param stack - + * @param error - Evaluation error if any + * @param scratch - + */ + constructor({ + line, + pc, + stack, + error, + scratch, + }: { + line: number | bigint; + pc: number | bigint; + stack: TealValue[]; + error?: string; + scratch?: TealValue[]; + }) { + super(); + this.line = line; + this.pc = pc; + this.stack = stack; + this.error = error; + this.scratch = scratch; + + this.attribute_map = { + line: 'line', + pc: 'pc', + stack: 'stack', + error: 'error', + scratch: 'scratch', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): DryrunState { + /* eslint-disable dot-notation */ + if (typeof data['line'] === 'undefined') + throw new Error(`Response is missing required field 'line': ${data}`); + if (typeof data['pc'] === 'undefined') + throw new Error(`Response is missing required field 'pc': ${data}`); + if (!Array.isArray(data['stack'])) + throw new Error( + `Response is missing required array field 'stack': ${data}` + ); + return new DryrunState({ + line: data['line'], + pc: data['pc'], + stack: data['stack'].map(TealValue.from_obj_for_encoding), + error: data['error'], + scratch: + typeof data['scratch'] !== 'undefined' + ? data['scratch'].map(TealValue.from_obj_for_encoding) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +/** + * DryrunTxnResult contains any LogicSig or ApplicationCall program debug + * information and state updates from a dryrun. + */ +export class DryrunTxnResult extends BaseModel { + /** + * Disassembled program line by line. + */ + public disassembly: string[]; + + public appCallMessages?: string[]; + + public appCallTrace?: DryrunState[]; + + /** + * Budget added during execution of app call transaction. + */ + public budgetAdded?: number | bigint; + + /** + * Budget consumed during execution of app call transaction. + */ + public budgetConsumed?: number | bigint; + + /** + * Application state delta. + */ + public globalDelta?: EvalDeltaKeyValue[]; + + public localDeltas?: AccountStateDelta[]; + + /** + * Disassembled lsig program line by line. + */ + public logicSigDisassembly?: string[]; + + public logicSigMessages?: string[]; + + public logicSigTrace?: DryrunState[]; + + public logs?: Uint8Array[]; + + /** + * Creates a new `DryrunTxnResult` object. + * @param disassembly - Disassembled program line by line. + * @param appCallMessages - + * @param appCallTrace - + * @param budgetAdded - Budget added during execution of app call transaction. + * @param budgetConsumed - Budget consumed during execution of app call transaction. + * @param globalDelta - Application state delta. + * @param localDeltas - + * @param logicSigDisassembly - Disassembled lsig program line by line. + * @param logicSigMessages - + * @param logicSigTrace - + * @param logs - + */ + constructor({ + disassembly, + appCallMessages, + appCallTrace, + budgetAdded, + budgetConsumed, + globalDelta, + localDeltas, + logicSigDisassembly, + logicSigMessages, + logicSigTrace, + logs, + }: { + disassembly: string[]; + appCallMessages?: string[]; + appCallTrace?: DryrunState[]; + budgetAdded?: number | bigint; + budgetConsumed?: number | bigint; + globalDelta?: EvalDeltaKeyValue[]; + localDeltas?: AccountStateDelta[]; + logicSigDisassembly?: string[]; + logicSigMessages?: string[]; + logicSigTrace?: DryrunState[]; + logs?: Uint8Array[]; + }) { + super(); + this.disassembly = disassembly; + this.appCallMessages = appCallMessages; + this.appCallTrace = appCallTrace; + this.budgetAdded = budgetAdded; + this.budgetConsumed = budgetConsumed; + this.globalDelta = globalDelta; + this.localDeltas = localDeltas; + this.logicSigDisassembly = logicSigDisassembly; + this.logicSigMessages = logicSigMessages; + this.logicSigTrace = logicSigTrace; + this.logs = logs; + + this.attribute_map = { + disassembly: 'disassembly', + appCallMessages: 'app-call-messages', + appCallTrace: 'app-call-trace', + budgetAdded: 'budget-added', + budgetConsumed: 'budget-consumed', + globalDelta: 'global-delta', + localDeltas: 'local-deltas', + logicSigDisassembly: 'logic-sig-disassembly', + logicSigMessages: 'logic-sig-messages', + logicSigTrace: 'logic-sig-trace', + logs: 'logs', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): DryrunTxnResult { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['disassembly'])) + throw new Error( + `Response is missing required array field 'disassembly': ${data}` + ); + return new DryrunTxnResult({ + disassembly: data['disassembly'], + appCallMessages: data['app-call-messages'], + appCallTrace: + typeof data['app-call-trace'] !== 'undefined' + ? data['app-call-trace'].map(DryrunState.from_obj_for_encoding) + : undefined, + budgetAdded: data['budget-added'], + budgetConsumed: data['budget-consumed'], + globalDelta: + typeof data['global-delta'] !== 'undefined' + ? data['global-delta'].map(EvalDeltaKeyValue.from_obj_for_encoding) + : undefined, + localDeltas: + typeof data['local-deltas'] !== 'undefined' + ? data['local-deltas'].map(AccountStateDelta.from_obj_for_encoding) + : undefined, + logicSigDisassembly: data['logic-sig-disassembly'], + logicSigMessages: data['logic-sig-messages'], + logicSigTrace: + typeof data['logic-sig-trace'] !== 'undefined' + ? data['logic-sig-trace'].map(DryrunState.from_obj_for_encoding) + : undefined, + logs: data['logs'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * An error response with optional data field. + */ +export class ErrorResponse extends BaseModel { + public message: string; + + public data?: Record; + + /** + * Creates a new `ErrorResponse` object. + * @param message - + * @param data - + */ + constructor({ + message, + data, + }: { + message: string; + data?: Record; + }) { + super(); + this.message = message; + this.data = data; + + this.attribute_map = { + message: 'message', + data: 'data', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): ErrorResponse { + /* eslint-disable dot-notation */ + if (typeof data['message'] === 'undefined') + throw new Error(`Response is missing required field 'message': ${data}`); + return new ErrorResponse({ + message: data['message'], + data: data['data'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Represents a TEAL value delta. + */ +export class EvalDelta extends BaseModel { + /** + * (at) delta action. + */ + public action: number | bigint; + + /** + * (bs) bytes value. + */ + public bytes?: string; + + /** + * (ui) uint value. + */ + public uint?: number | bigint; + + /** + * Creates a new `EvalDelta` object. + * @param action - (at) delta action. + * @param bytes - (bs) bytes value. + * @param uint - (ui) uint value. + */ + constructor({ + action, + bytes, + uint, + }: { + action: number | bigint; + bytes?: string; + uint?: number | bigint; + }) { + super(); + this.action = action; + this.bytes = bytes; + this.uint = uint; + + this.attribute_map = { + action: 'action', + bytes: 'bytes', + uint: 'uint', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): EvalDelta { + /* eslint-disable dot-notation */ + if (typeof data['action'] === 'undefined') + throw new Error(`Response is missing required field 'action': ${data}`); + return new EvalDelta({ + action: data['action'], + bytes: data['bytes'], + uint: data['uint'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Key-value pairs for StateDelta. + */ +export class EvalDeltaKeyValue extends BaseModel { + public key: string; + + /** + * Represents a TEAL value delta. + */ + public value: EvalDelta; + + /** + * Creates a new `EvalDeltaKeyValue` object. + * @param key - + * @param value - Represents a TEAL value delta. + */ + constructor({ key, value }: { key: string; value: EvalDelta }) { + super(); + this.key = key; + this.value = value; + + this.attribute_map = { + key: 'key', + value: 'value', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): EvalDeltaKeyValue { + /* eslint-disable dot-notation */ + if (typeof data['key'] === 'undefined') + throw new Error(`Response is missing required field 'key': ${data}`); + if (typeof data['value'] === 'undefined') + throw new Error(`Response is missing required field 'value': ${data}`); + return new EvalDeltaKeyValue({ + key: data['key'], + value: EvalDelta.from_obj_for_encoding(data['value']), + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Response containing the timestamp offset in seconds + */ +export class GetBlockTimeStampOffsetResponse extends BaseModel { + /** + * Timestamp offset in seconds. + */ + public offset: number | bigint; + + /** + * Creates a new `GetBlockTimeStampOffsetResponse` object. + * @param offset - Timestamp offset in seconds. + */ + constructor({ offset }: { offset: number | bigint }) { + super(); + this.offset = offset; + + this.attribute_map = { + offset: 'offset', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): GetBlockTimeStampOffsetResponse { + /* eslint-disable dot-notation */ + if (typeof data['offset'] === 'undefined') + throw new Error(`Response is missing required field 'offset': ${data}`); + return new GetBlockTimeStampOffsetResponse({ + offset: data['offset'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Response containing the ledger's minimum sync round + */ +export class GetSyncRoundResponse extends BaseModel { + /** + * The minimum sync round for the ledger. + */ + public round: number | bigint; + + /** + * Creates a new `GetSyncRoundResponse` object. + * @param round - The minimum sync round for the ledger. + */ + constructor({ round }: { round: number | bigint }) { + super(); + this.round = round; + + this.attribute_map = { + round: 'round', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): GetSyncRoundResponse { + /* eslint-disable dot-notation */ + if (typeof data['round'] === 'undefined') + throw new Error(`Response is missing required field 'round': ${data}`); + return new GetSyncRoundResponse({ + round: data['round'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * A single Delta containing the key, the previous value and the current value for + * a single round. + */ +export class KvDelta extends BaseModel { + /** + * The key, base64 encoded. + */ + public key?: Uint8Array; + + /** + * The new value of the KV store entry, base64 encoded. + */ + public value?: Uint8Array; + + /** + * Creates a new `KvDelta` object. + * @param key - The key, base64 encoded. + * @param value - The new value of the KV store entry, base64 encoded. + */ + constructor({ + key, + value, + }: { + key?: string | Uint8Array; + value?: string | Uint8Array; + }) { + super(); + this.key = + typeof key === 'string' + ? new Uint8Array(Buffer.from(key, 'base64')) + : key; + this.value = + typeof value === 'string' + ? new Uint8Array(Buffer.from(value, 'base64')) + : value; + + this.attribute_map = { + key: 'key', + value: 'value', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): KvDelta { + /* eslint-disable dot-notation */ + return new KvDelta({ + key: data['key'], + value: data['value'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Contains a ledger delta for a single transaction group + */ +export class LedgerStateDeltaForTransactionGroup extends BaseModel { + /** + * Ledger StateDelta object + */ + public delta: Record; + + public ids: string[]; + + /** + * Creates a new `LedgerStateDeltaForTransactionGroup` object. + * @param delta - Ledger StateDelta object + * @param ids - + */ + constructor({ delta, ids }: { delta: Record; ids: string[] }) { + super(); + this.delta = delta; + this.ids = ids; + + this.attribute_map = { + delta: 'delta', + ids: 'ids', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): LedgerStateDeltaForTransactionGroup { + /* eslint-disable dot-notation */ + if (typeof data['delta'] === 'undefined') + throw new Error(`Response is missing required field 'delta': ${data}`); + if (!Array.isArray(data['ids'])) + throw new Error( + `Response is missing required array field 'ids': ${data}` + ); + return new LedgerStateDeltaForTransactionGroup({ + delta: data['delta'], + ids: data['ids'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Proof of membership and position of a light block header. + */ +export class LightBlockHeaderProof extends BaseModel { + /** + * The index of the light block header in the vector commitment tree + */ + public index: number | bigint; + + /** + * The encoded proof. + */ + public proof: Uint8Array; + + /** + * Represents the depth of the tree that is being proven, i.e. the number of edges + * from a leaf to the root. + */ + public treedepth: number | bigint; + + /** + * Creates a new `LightBlockHeaderProof` object. + * @param index - The index of the light block header in the vector commitment tree + * @param proof - The encoded proof. + * @param treedepth - Represents the depth of the tree that is being proven, i.e. the number of edges + * from a leaf to the root. + */ + constructor({ + index, + proof, + treedepth, + }: { + index: number | bigint; + proof: string | Uint8Array; + treedepth: number | bigint; + }) { + super(); + this.index = index; + this.proof = + typeof proof === 'string' + ? new Uint8Array(Buffer.from(proof, 'base64')) + : proof; + this.treedepth = treedepth; + + this.attribute_map = { + index: 'index', + proof: 'proof', + treedepth: 'treedepth', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): LightBlockHeaderProof { + /* eslint-disable dot-notation */ + if (typeof data['index'] === 'undefined') + throw new Error(`Response is missing required field 'index': ${data}`); + if (typeof data['proof'] === 'undefined') + throw new Error(`Response is missing required field 'proof': ${data}`); + if (typeof data['treedepth'] === 'undefined') + throw new Error( + `Response is missing required field 'treedepth': ${data}` + ); + return new LightBlockHeaderProof({ + index: data['index'], + proof: data['proof'], + treedepth: data['treedepth'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class NodeStatusResponse extends BaseModel { + /** + * CatchupTime in nanoseconds + */ + public catchupTime: number | bigint; + + /** + * LastRound indicates the last round seen + */ + public lastRound: number | bigint; + + /** + * LastVersion indicates the last consensus version supported + */ + public lastVersion: string; + + /** + * NextVersion of consensus protocol to use + */ + public nextVersion: string; + + /** + * NextVersionRound is the round at which the next consensus version will apply + */ + public nextVersionRound: number | bigint; + + /** + * NextVersionSupported indicates whether the next consensus version is supported + * by this node + */ + public nextVersionSupported: boolean; + + /** + * StoppedAtUnsupportedRound indicates that the node does not support the new + * rounds and has stopped making progress + */ + public stoppedAtUnsupportedRound: boolean; + + /** + * TimeSinceLastRound in nanoseconds + */ + public timeSinceLastRound: number | bigint; + + /** + * The current catchpoint that is being caught up to + */ + public catchpoint?: string; + + /** + * The number of blocks that have already been obtained by the node as part of the + * catchup + */ + public catchpointAcquiredBlocks?: number | bigint; + + /** + * The number of accounts from the current catchpoint that have been processed so + * far as part of the catchup + */ + public catchpointProcessedAccounts?: number | bigint; + + /** + * The number of key-values (KVs) from the current catchpoint that have been + * processed so far as part of the catchup + */ + public catchpointProcessedKvs?: number | bigint; + + /** + * The total number of accounts included in the current catchpoint + */ + public catchpointTotalAccounts?: number | bigint; + + /** + * The total number of blocks that are required to complete the current catchpoint + * catchup + */ + public catchpointTotalBlocks?: number | bigint; + + /** + * The total number of key-values (KVs) included in the current catchpoint + */ + public catchpointTotalKvs?: number | bigint; + + /** + * The number of accounts from the current catchpoint that have been verified so + * far as part of the catchup + */ + public catchpointVerifiedAccounts?: number | bigint; + + /** + * The number of key-values (KVs) from the current catchpoint that have been + * verified so far as part of the catchup + */ + public catchpointVerifiedKvs?: number | bigint; + + /** + * The last catchpoint seen by the node + */ + public lastCatchpoint?: string; + + /** + * Upgrade delay + */ + public upgradeDelay?: number | bigint; + + /** + * Next protocol round + */ + public upgradeNextProtocolVoteBefore?: number | bigint; + + /** + * No votes cast for consensus upgrade + */ + public upgradeNoVotes?: number | bigint; + + /** + * This node's upgrade vote + */ + public upgradeNodeVote?: boolean; + + /** + * Total voting rounds for current upgrade + */ + public upgradeVoteRounds?: number | bigint; + + /** + * Total votes cast for consensus upgrade + */ + public upgradeVotes?: number | bigint; + + /** + * Yes votes required for consensus upgrade + */ + public upgradeVotesRequired?: number | bigint; + + /** + * Yes votes cast for consensus upgrade + */ + public upgradeYesVotes?: number | bigint; + + /** + * Creates a new `NodeStatusResponse` object. + * @param catchupTime - CatchupTime in nanoseconds + * @param lastRound - LastRound indicates the last round seen + * @param lastVersion - LastVersion indicates the last consensus version supported + * @param nextVersion - NextVersion of consensus protocol to use + * @param nextVersionRound - NextVersionRound is the round at which the next consensus version will apply + * @param nextVersionSupported - NextVersionSupported indicates whether the next consensus version is supported + * by this node + * @param stoppedAtUnsupportedRound - StoppedAtUnsupportedRound indicates that the node does not support the new + * rounds and has stopped making progress + * @param timeSinceLastRound - TimeSinceLastRound in nanoseconds + * @param catchpoint - The current catchpoint that is being caught up to + * @param catchpointAcquiredBlocks - The number of blocks that have already been obtained by the node as part of the + * catchup + * @param catchpointProcessedAccounts - The number of accounts from the current catchpoint that have been processed so + * far as part of the catchup + * @param catchpointProcessedKvs - The number of key-values (KVs) from the current catchpoint that have been + * processed so far as part of the catchup + * @param catchpointTotalAccounts - The total number of accounts included in the current catchpoint + * @param catchpointTotalBlocks - The total number of blocks that are required to complete the current catchpoint + * catchup + * @param catchpointTotalKvs - The total number of key-values (KVs) included in the current catchpoint + * @param catchpointVerifiedAccounts - The number of accounts from the current catchpoint that have been verified so + * far as part of the catchup + * @param catchpointVerifiedKvs - The number of key-values (KVs) from the current catchpoint that have been + * verified so far as part of the catchup + * @param lastCatchpoint - The last catchpoint seen by the node + * @param upgradeDelay - Upgrade delay + * @param upgradeNextProtocolVoteBefore - Next protocol round + * @param upgradeNoVotes - No votes cast for consensus upgrade + * @param upgradeNodeVote - This node's upgrade vote + * @param upgradeVoteRounds - Total voting rounds for current upgrade + * @param upgradeVotes - Total votes cast for consensus upgrade + * @param upgradeVotesRequired - Yes votes required for consensus upgrade + * @param upgradeYesVotes - Yes votes cast for consensus upgrade + */ + constructor({ + catchupTime, + lastRound, + lastVersion, + nextVersion, + nextVersionRound, + nextVersionSupported, + stoppedAtUnsupportedRound, + timeSinceLastRound, + catchpoint, + catchpointAcquiredBlocks, + catchpointProcessedAccounts, + catchpointProcessedKvs, + catchpointTotalAccounts, + catchpointTotalBlocks, + catchpointTotalKvs, + catchpointVerifiedAccounts, + catchpointVerifiedKvs, + lastCatchpoint, + upgradeDelay, + upgradeNextProtocolVoteBefore, + upgradeNoVotes, + upgradeNodeVote, + upgradeVoteRounds, + upgradeVotes, + upgradeVotesRequired, + upgradeYesVotes, + }: { + catchupTime: number | bigint; + lastRound: number | bigint; + lastVersion: string; + nextVersion: string; + nextVersionRound: number | bigint; + nextVersionSupported: boolean; + stoppedAtUnsupportedRound: boolean; + timeSinceLastRound: number | bigint; + catchpoint?: string; + catchpointAcquiredBlocks?: number | bigint; + catchpointProcessedAccounts?: number | bigint; + catchpointProcessedKvs?: number | bigint; + catchpointTotalAccounts?: number | bigint; + catchpointTotalBlocks?: number | bigint; + catchpointTotalKvs?: number | bigint; + catchpointVerifiedAccounts?: number | bigint; + catchpointVerifiedKvs?: number | bigint; + lastCatchpoint?: string; + upgradeDelay?: number | bigint; + upgradeNextProtocolVoteBefore?: number | bigint; + upgradeNoVotes?: number | bigint; + upgradeNodeVote?: boolean; + upgradeVoteRounds?: number | bigint; + upgradeVotes?: number | bigint; + upgradeVotesRequired?: number | bigint; + upgradeYesVotes?: number | bigint; + }) { + super(); + this.catchupTime = catchupTime; + this.lastRound = lastRound; + this.lastVersion = lastVersion; + this.nextVersion = nextVersion; + this.nextVersionRound = nextVersionRound; + this.nextVersionSupported = nextVersionSupported; + this.stoppedAtUnsupportedRound = stoppedAtUnsupportedRound; + this.timeSinceLastRound = timeSinceLastRound; + this.catchpoint = catchpoint; + this.catchpointAcquiredBlocks = catchpointAcquiredBlocks; + this.catchpointProcessedAccounts = catchpointProcessedAccounts; + this.catchpointProcessedKvs = catchpointProcessedKvs; + this.catchpointTotalAccounts = catchpointTotalAccounts; + this.catchpointTotalBlocks = catchpointTotalBlocks; + this.catchpointTotalKvs = catchpointTotalKvs; + this.catchpointVerifiedAccounts = catchpointVerifiedAccounts; + this.catchpointVerifiedKvs = catchpointVerifiedKvs; + this.lastCatchpoint = lastCatchpoint; + this.upgradeDelay = upgradeDelay; + this.upgradeNextProtocolVoteBefore = upgradeNextProtocolVoteBefore; + this.upgradeNoVotes = upgradeNoVotes; + this.upgradeNodeVote = upgradeNodeVote; + this.upgradeVoteRounds = upgradeVoteRounds; + this.upgradeVotes = upgradeVotes; + this.upgradeVotesRequired = upgradeVotesRequired; + this.upgradeYesVotes = upgradeYesVotes; + + this.attribute_map = { + catchupTime: 'catchup-time', + lastRound: 'last-round', + lastVersion: 'last-version', + nextVersion: 'next-version', + nextVersionRound: 'next-version-round', + nextVersionSupported: 'next-version-supported', + stoppedAtUnsupportedRound: 'stopped-at-unsupported-round', + timeSinceLastRound: 'time-since-last-round', + catchpoint: 'catchpoint', + catchpointAcquiredBlocks: 'catchpoint-acquired-blocks', + catchpointProcessedAccounts: 'catchpoint-processed-accounts', + catchpointProcessedKvs: 'catchpoint-processed-kvs', + catchpointTotalAccounts: 'catchpoint-total-accounts', + catchpointTotalBlocks: 'catchpoint-total-blocks', + catchpointTotalKvs: 'catchpoint-total-kvs', + catchpointVerifiedAccounts: 'catchpoint-verified-accounts', + catchpointVerifiedKvs: 'catchpoint-verified-kvs', + lastCatchpoint: 'last-catchpoint', + upgradeDelay: 'upgrade-delay', + upgradeNextProtocolVoteBefore: 'upgrade-next-protocol-vote-before', + upgradeNoVotes: 'upgrade-no-votes', + upgradeNodeVote: 'upgrade-node-vote', + upgradeVoteRounds: 'upgrade-vote-rounds', + upgradeVotes: 'upgrade-votes', + upgradeVotesRequired: 'upgrade-votes-required', + upgradeYesVotes: 'upgrade-yes-votes', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): NodeStatusResponse { + /* eslint-disable dot-notation */ + if (typeof data['catchup-time'] === 'undefined') + throw new Error( + `Response is missing required field 'catchup-time': ${data}` + ); + if (typeof data['last-round'] === 'undefined') + throw new Error( + `Response is missing required field 'last-round': ${data}` + ); + if (typeof data['last-version'] === 'undefined') + throw new Error( + `Response is missing required field 'last-version': ${data}` + ); + if (typeof data['next-version'] === 'undefined') + throw new Error( + `Response is missing required field 'next-version': ${data}` + ); + if (typeof data['next-version-round'] === 'undefined') + throw new Error( + `Response is missing required field 'next-version-round': ${data}` + ); + if (typeof data['next-version-supported'] === 'undefined') + throw new Error( + `Response is missing required field 'next-version-supported': ${data}` + ); + if (typeof data['stopped-at-unsupported-round'] === 'undefined') + throw new Error( + `Response is missing required field 'stopped-at-unsupported-round': ${data}` + ); + if (typeof data['time-since-last-round'] === 'undefined') + throw new Error( + `Response is missing required field 'time-since-last-round': ${data}` + ); + return new NodeStatusResponse({ + catchupTime: data['catchup-time'], + lastRound: data['last-round'], + lastVersion: data['last-version'], + nextVersion: data['next-version'], + nextVersionRound: data['next-version-round'], + nextVersionSupported: data['next-version-supported'], + stoppedAtUnsupportedRound: data['stopped-at-unsupported-round'], + timeSinceLastRound: data['time-since-last-round'], + catchpoint: data['catchpoint'], + catchpointAcquiredBlocks: data['catchpoint-acquired-blocks'], + catchpointProcessedAccounts: data['catchpoint-processed-accounts'], + catchpointProcessedKvs: data['catchpoint-processed-kvs'], + catchpointTotalAccounts: data['catchpoint-total-accounts'], + catchpointTotalBlocks: data['catchpoint-total-blocks'], + catchpointTotalKvs: data['catchpoint-total-kvs'], + catchpointVerifiedAccounts: data['catchpoint-verified-accounts'], + catchpointVerifiedKvs: data['catchpoint-verified-kvs'], + lastCatchpoint: data['last-catchpoint'], + upgradeDelay: data['upgrade-delay'], + upgradeNextProtocolVoteBefore: data['upgrade-next-protocol-vote-before'], + upgradeNoVotes: data['upgrade-no-votes'], + upgradeNodeVote: data['upgrade-node-vote'], + upgradeVoteRounds: data['upgrade-vote-rounds'], + upgradeVotes: data['upgrade-votes'], + upgradeVotesRequired: data['upgrade-votes-required'], + upgradeYesVotes: data['upgrade-yes-votes'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Details about a pending transaction. If the transaction was recently confirmed, + * includes confirmation details like the round and reward details. + */ +export class PendingTransactionResponse extends BaseModel { + /** + * Indicates that the transaction was kicked out of this node's transaction pool + * (and specifies why that happened). An empty string indicates the transaction + * wasn't kicked out of this node's txpool due to an error. + */ + public poolError: string; + + /** + * The raw signed transaction. + */ + public txn: EncodedSignedTransaction; + + /** + * The application index if the transaction was found and it created an + * application. + */ + public applicationIndex?: number | bigint; + + /** + * The number of the asset's unit that were transferred to the close-to address. + */ + public assetClosingAmount?: number | bigint; + + /** + * The asset index if the transaction was found and it created an asset. + */ + public assetIndex?: number | bigint; + + /** + * Rewards in microalgos applied to the close remainder to account. + */ + public closeRewards?: number | bigint; + + /** + * Closing amount for the transaction. + */ + public closingAmount?: number | bigint; + + /** + * The round where this transaction was confirmed, if present. + */ + public confirmedRound?: number | bigint; + + /** + * Global state key/value changes for the application being executed by this + * transaction. + */ + public globalStateDelta?: EvalDeltaKeyValue[]; + + /** + * Inner transactions produced by application execution. + */ + public innerTxns?: PendingTransactionResponse[]; + + /** + * Local state key/value changes for the application being executed by this + * transaction. + */ + public localStateDelta?: AccountStateDelta[]; + + /** + * Logs for the application being executed by this transaction. + */ + public logs?: Uint8Array[]; + + /** + * Rewards in microalgos applied to the receiver account. + */ + public receiverRewards?: number | bigint; + + /** + * Rewards in microalgos applied to the sender account. + */ + public senderRewards?: number | bigint; + + /** + * Creates a new `PendingTransactionResponse` object. + * @param poolError - Indicates that the transaction was kicked out of this node's transaction pool + * (and specifies why that happened). An empty string indicates the transaction + * wasn't kicked out of this node's txpool due to an error. + * @param txn - The raw signed transaction. + * @param applicationIndex - The application index if the transaction was found and it created an + * application. + * @param assetClosingAmount - The number of the asset's unit that were transferred to the close-to address. + * @param assetIndex - The asset index if the transaction was found and it created an asset. + * @param closeRewards - Rewards in microalgos applied to the close remainder to account. + * @param closingAmount - Closing amount for the transaction. + * @param confirmedRound - The round where this transaction was confirmed, if present. + * @param globalStateDelta - Global state key/value changes for the application being executed by this + * transaction. + * @param innerTxns - Inner transactions produced by application execution. + * @param localStateDelta - Local state key/value changes for the application being executed by this + * transaction. + * @param logs - Logs for the application being executed by this transaction. + * @param receiverRewards - Rewards in microalgos applied to the receiver account. + * @param senderRewards - Rewards in microalgos applied to the sender account. + */ + constructor({ + poolError, + txn, + applicationIndex, + assetClosingAmount, + assetIndex, + closeRewards, + closingAmount, + confirmedRound, + globalStateDelta, + innerTxns, + localStateDelta, + logs, + receiverRewards, + senderRewards, + }: { + poolError: string; + txn: EncodedSignedTransaction; + applicationIndex?: number | bigint; + assetClosingAmount?: number | bigint; + assetIndex?: number | bigint; + closeRewards?: number | bigint; + closingAmount?: number | bigint; + confirmedRound?: number | bigint; + globalStateDelta?: EvalDeltaKeyValue[]; + innerTxns?: PendingTransactionResponse[]; + localStateDelta?: AccountStateDelta[]; + logs?: Uint8Array[]; + receiverRewards?: number | bigint; + senderRewards?: number | bigint; + }) { + super(); + this.poolError = poolError; + this.txn = txn; + this.applicationIndex = applicationIndex; + this.assetClosingAmount = assetClosingAmount; + this.assetIndex = assetIndex; + this.closeRewards = closeRewards; + this.closingAmount = closingAmount; + this.confirmedRound = confirmedRound; + this.globalStateDelta = globalStateDelta; + this.innerTxns = innerTxns; + this.localStateDelta = localStateDelta; + this.logs = logs; + this.receiverRewards = receiverRewards; + this.senderRewards = senderRewards; + + this.attribute_map = { + poolError: 'pool-error', + txn: 'txn', + applicationIndex: 'application-index', + assetClosingAmount: 'asset-closing-amount', + assetIndex: 'asset-index', + closeRewards: 'close-rewards', + closingAmount: 'closing-amount', + confirmedRound: 'confirmed-round', + globalStateDelta: 'global-state-delta', + innerTxns: 'inner-txns', + localStateDelta: 'local-state-delta', + logs: 'logs', + receiverRewards: 'receiver-rewards', + senderRewards: 'sender-rewards', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): PendingTransactionResponse { + /* eslint-disable dot-notation */ + if (typeof data['pool-error'] === 'undefined') + throw new Error( + `Response is missing required field 'pool-error': ${data}` + ); + if (typeof data['txn'] === 'undefined') + throw new Error(`Response is missing required field 'txn': ${data}`); + return new PendingTransactionResponse({ + poolError: data['pool-error'], + txn: data['txn'], + applicationIndex: data['application-index'], + assetClosingAmount: data['asset-closing-amount'], + assetIndex: data['asset-index'], + closeRewards: data['close-rewards'], + closingAmount: data['closing-amount'], + confirmedRound: data['confirmed-round'], + globalStateDelta: + typeof data['global-state-delta'] !== 'undefined' + ? data['global-state-delta'].map( + EvalDeltaKeyValue.from_obj_for_encoding + ) + : undefined, + innerTxns: + typeof data['inner-txns'] !== 'undefined' + ? data['inner-txns'].map( + PendingTransactionResponse.from_obj_for_encoding + ) + : undefined, + localStateDelta: + typeof data['local-state-delta'] !== 'undefined' + ? data['local-state-delta'].map( + AccountStateDelta.from_obj_for_encoding + ) + : undefined, + logs: data['logs'], + receiverRewards: data['receiver-rewards'], + senderRewards: data['sender-rewards'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * A potentially truncated list of transactions currently in the node's transaction + * pool. You can compute whether or not the list is truncated if the number of + * elements in the **top-transactions** array is fewer than **total-transactions**. + */ +export class PendingTransactionsResponse extends BaseModel { + /** + * An array of signed transaction objects. + */ + public topTransactions: EncodedSignedTransaction[]; + + /** + * Total number of transactions in the pool. + */ + public totalTransactions: number | bigint; + + /** + * Creates a new `PendingTransactionsResponse` object. + * @param topTransactions - An array of signed transaction objects. + * @param totalTransactions - Total number of transactions in the pool. + */ + constructor({ + topTransactions, + totalTransactions, + }: { + topTransactions: EncodedSignedTransaction[]; + totalTransactions: number | bigint; + }) { + super(); + this.topTransactions = topTransactions; + this.totalTransactions = totalTransactions; + + this.attribute_map = { + topTransactions: 'top-transactions', + totalTransactions: 'total-transactions', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): PendingTransactionsResponse { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['top-transactions'])) + throw new Error( + `Response is missing required array field 'top-transactions': ${data}` + ); + if (typeof data['total-transactions'] === 'undefined') + throw new Error( + `Response is missing required field 'total-transactions': ${data}` + ); + return new PendingTransactionsResponse({ + topTransactions: data['top-transactions'], + totalTransactions: data['total-transactions'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Transaction ID of the submission. + */ +export class PostTransactionsResponse extends BaseModel { + /** + * encoding of the transaction hash. + */ + public txid: string; + + /** + * Creates a new `PostTransactionsResponse` object. + * @param txid - encoding of the transaction hash. + */ + constructor({ txid }: { txid: string }) { + super(); + this.txid = txid; + + this.attribute_map = { + txid: 'txId', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): PostTransactionsResponse { + /* eslint-disable dot-notation */ + if (typeof data['txId'] === 'undefined') + throw new Error(`Response is missing required field 'txId': ${data}`); + return new PostTransactionsResponse({ + txid: data['txId'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Request type for simulation endpoint. + */ +export class SimulateRequest extends BaseModel { + /** + * The transaction groups to simulate. + */ + public txnGroups: SimulateRequestTransactionGroup[]; + + /** + * Allow transactions without signatures to be simulated as if they had correct + * signatures. + */ + public allowEmptySignatures?: boolean; + + /** + * Lifts limits on log opcode usage during simulation. + */ + public allowMoreLogging?: boolean; + + /** + * Applies extra opcode budget during simulation for each transaction group. + */ + public extraOpcodeBudget?: number | bigint; + + /** + * Creates a new `SimulateRequest` object. + * @param txnGroups - The transaction groups to simulate. + * @param allowEmptySignatures - Allow transactions without signatures to be simulated as if they had correct + * signatures. + * @param allowMoreLogging - Lifts limits on log opcode usage during simulation. + * @param extraOpcodeBudget - Applies extra opcode budget during simulation for each transaction group. + */ + constructor({ + txnGroups, + allowEmptySignatures, + allowMoreLogging, + extraOpcodeBudget, + }: { + txnGroups: SimulateRequestTransactionGroup[]; + allowEmptySignatures?: boolean; + allowMoreLogging?: boolean; + extraOpcodeBudget?: number | bigint; + }) { + super(); + this.txnGroups = txnGroups; + this.allowEmptySignatures = allowEmptySignatures; + this.allowMoreLogging = allowMoreLogging; + this.extraOpcodeBudget = extraOpcodeBudget; + + this.attribute_map = { + txnGroups: 'txn-groups', + allowEmptySignatures: 'allow-empty-signatures', + allowMoreLogging: 'allow-more-logging', + extraOpcodeBudget: 'extra-opcode-budget', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): SimulateRequest { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['txn-groups'])) + throw new Error( + `Response is missing required array field 'txn-groups': ${data}` + ); + return new SimulateRequest({ + txnGroups: data['txn-groups'].map( + SimulateRequestTransactionGroup.from_obj_for_encoding + ), + allowEmptySignatures: data['allow-empty-signatures'], + allowMoreLogging: data['allow-more-logging'], + extraOpcodeBudget: data['extra-opcode-budget'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * A transaction group to simulate. + */ +export class SimulateRequestTransactionGroup extends BaseModel { + /** + * An atomic transaction group. + */ + public txns: EncodedSignedTransaction[]; + + /** + * Creates a new `SimulateRequestTransactionGroup` object. + * @param txns - An atomic transaction group. + */ + constructor({ txns }: { txns: EncodedSignedTransaction[] }) { + super(); + this.txns = txns; + + this.attribute_map = { + txns: 'txns', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): SimulateRequestTransactionGroup { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['txns'])) + throw new Error( + `Response is missing required array field 'txns': ${data}` + ); + return new SimulateRequestTransactionGroup({ + txns: data['txns'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Result of a transaction group simulation. + */ +export class SimulateResponse extends BaseModel { + /** + * The round immediately preceding this simulation. State changes through this + * round were used to run this simulation. + */ + public lastRound: number | bigint; + + /** + * A result object for each transaction group that was simulated. + */ + public txnGroups: SimulateTransactionGroupResult[]; + + /** + * The version of this response object. + */ + public version: number | bigint; + + /** + * The set of parameters and limits override during simulation. If this set of + * parameters is present, then evaluation parameters may differ from standard + * evaluation in certain ways. + */ + public evalOverrides?: SimulationEvalOverrides; + + /** + * Creates a new `SimulateResponse` object. + * @param lastRound - The round immediately preceding this simulation. State changes through this + * round were used to run this simulation. + * @param txnGroups - A result object for each transaction group that was simulated. + * @param version - The version of this response object. + * @param evalOverrides - The set of parameters and limits override during simulation. If this set of + * parameters is present, then evaluation parameters may differ from standard + * evaluation in certain ways. + */ + constructor({ + lastRound, + txnGroups, + version, + evalOverrides, + }: { + lastRound: number | bigint; + txnGroups: SimulateTransactionGroupResult[]; + version: number | bigint; + evalOverrides?: SimulationEvalOverrides; + }) { + super(); + this.lastRound = lastRound; + this.txnGroups = txnGroups; + this.version = version; + this.evalOverrides = evalOverrides; + + this.attribute_map = { + lastRound: 'last-round', + txnGroups: 'txn-groups', + version: 'version', + evalOverrides: 'eval-overrides', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): SimulateResponse { + /* eslint-disable dot-notation */ + if (typeof data['last-round'] === 'undefined') + throw new Error( + `Response is missing required field 'last-round': ${data}` + ); + if (!Array.isArray(data['txn-groups'])) + throw new Error( + `Response is missing required array field 'txn-groups': ${data}` + ); + if (typeof data['version'] === 'undefined') + throw new Error(`Response is missing required field 'version': ${data}`); + return new SimulateResponse({ + lastRound: data['last-round'], + txnGroups: data['txn-groups'].map( + SimulateTransactionGroupResult.from_obj_for_encoding + ), + version: data['version'], + evalOverrides: + typeof data['eval-overrides'] !== 'undefined' + ? SimulationEvalOverrides.from_obj_for_encoding( + data['eval-overrides'] + ) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Simulation result for an atomic transaction group + */ +export class SimulateTransactionGroupResult extends BaseModel { + /** + * Simulation result for individual transactions + */ + public txnResults: SimulateTransactionResult[]; + + /** + * Total budget added during execution of app calls in the transaction group. + */ + public appBudgetAdded?: number | bigint; + + /** + * Total budget consumed during execution of app calls in the transaction group. + */ + public appBudgetConsumed?: number | bigint; + + /** + * If present, indicates which transaction in this group caused the failure. This + * array represents the path to the failing transaction. Indexes are zero based, + * the first element indicates the top-level transaction, and successive elements + * indicate deeper inner transactions. + */ + public failedAt?: (number | bigint)[]; + + /** + * If present, indicates that the transaction group failed and specifies why that + * happened + */ + public failureMessage?: string; + + /** + * Creates a new `SimulateTransactionGroupResult` object. + * @param txnResults - Simulation result for individual transactions + * @param appBudgetAdded - Total budget added during execution of app calls in the transaction group. + * @param appBudgetConsumed - Total budget consumed during execution of app calls in the transaction group. + * @param failedAt - If present, indicates which transaction in this group caused the failure. This + * array represents the path to the failing transaction. Indexes are zero based, + * the first element indicates the top-level transaction, and successive elements + * indicate deeper inner transactions. + * @param failureMessage - If present, indicates that the transaction group failed and specifies why that + * happened + */ + constructor({ + txnResults, + appBudgetAdded, + appBudgetConsumed, + failedAt, + failureMessage, + }: { + txnResults: SimulateTransactionResult[]; + appBudgetAdded?: number | bigint; + appBudgetConsumed?: number | bigint; + failedAt?: (number | bigint)[]; + failureMessage?: string; + }) { + super(); + this.txnResults = txnResults; + this.appBudgetAdded = appBudgetAdded; + this.appBudgetConsumed = appBudgetConsumed; + this.failedAt = failedAt; + this.failureMessage = failureMessage; + + this.attribute_map = { + txnResults: 'txn-results', + appBudgetAdded: 'app-budget-added', + appBudgetConsumed: 'app-budget-consumed', + failedAt: 'failed-at', + failureMessage: 'failure-message', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): SimulateTransactionGroupResult { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['txn-results'])) + throw new Error( + `Response is missing required array field 'txn-results': ${data}` + ); + return new SimulateTransactionGroupResult({ + txnResults: data['txn-results'].map( + SimulateTransactionResult.from_obj_for_encoding + ), + appBudgetAdded: data['app-budget-added'], + appBudgetConsumed: data['app-budget-consumed'], + failedAt: data['failed-at'], + failureMessage: data['failure-message'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Simulation result for an individual transaction + */ +export class SimulateTransactionResult extends BaseModel { + /** + * Details about a pending transaction. If the transaction was recently confirmed, + * includes confirmation details like the round and reward details. + */ + public txnResult: PendingTransactionResponse; + + /** + * Budget used during execution of an app call transaction. This value includes + * budged used by inner app calls spawned by this transaction. + */ + public appBudgetConsumed?: number | bigint; + + /** + * Budget used during execution of a logic sig transaction. + */ + public logicSigBudgetConsumed?: number | bigint; + + /** + * Creates a new `SimulateTransactionResult` object. + * @param txnResult - Details about a pending transaction. If the transaction was recently confirmed, + * includes confirmation details like the round and reward details. + * @param appBudgetConsumed - Budget used during execution of an app call transaction. This value includes + * budged used by inner app calls spawned by this transaction. + * @param logicSigBudgetConsumed - Budget used during execution of a logic sig transaction. + */ + constructor({ + txnResult, + appBudgetConsumed, + logicSigBudgetConsumed, + }: { + txnResult: PendingTransactionResponse; + appBudgetConsumed?: number | bigint; + logicSigBudgetConsumed?: number | bigint; + }) { + super(); + this.txnResult = txnResult; + this.appBudgetConsumed = appBudgetConsumed; + this.logicSigBudgetConsumed = logicSigBudgetConsumed; + + this.attribute_map = { + txnResult: 'txn-result', + appBudgetConsumed: 'app-budget-consumed', + logicSigBudgetConsumed: 'logic-sig-budget-consumed', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): SimulateTransactionResult { + /* eslint-disable dot-notation */ + if (typeof data['txn-result'] === 'undefined') + throw new Error( + `Response is missing required field 'txn-result': ${data}` + ); + return new SimulateTransactionResult({ + txnResult: PendingTransactionResponse.from_obj_for_encoding( + data['txn-result'] + ), + appBudgetConsumed: data['app-budget-consumed'], + logicSigBudgetConsumed: data['logic-sig-budget-consumed'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * The set of parameters and limits override during simulation. If this set of + * parameters is present, then evaluation parameters may differ from standard + * evaluation in certain ways. + */ +export class SimulationEvalOverrides extends BaseModel { + /** + * If true, transactions without signatures are allowed and simulated as if they + * were properly signed. + */ + public allowEmptySignatures?: boolean; + + /** + * The extra opcode budget added to each transaction group during simulation + */ + public extraOpcodeBudget?: number | bigint; + + /** + * The maximum log calls one can make during simulation + */ + public maxLogCalls?: number | bigint; + + /** + * The maximum byte number to log during simulation + */ + public maxLogSize?: number | bigint; + + /** + * Creates a new `SimulationEvalOverrides` object. + * @param allowEmptySignatures - If true, transactions without signatures are allowed and simulated as if they + * were properly signed. + * @param extraOpcodeBudget - The extra opcode budget added to each transaction group during simulation + * @param maxLogCalls - The maximum log calls one can make during simulation + * @param maxLogSize - The maximum byte number to log during simulation + */ + constructor({ + allowEmptySignatures, + extraOpcodeBudget, + maxLogCalls, + maxLogSize, + }: { + allowEmptySignatures?: boolean; + extraOpcodeBudget?: number | bigint; + maxLogCalls?: number | bigint; + maxLogSize?: number | bigint; + }) { + super(); + this.allowEmptySignatures = allowEmptySignatures; + this.extraOpcodeBudget = extraOpcodeBudget; + this.maxLogCalls = maxLogCalls; + this.maxLogSize = maxLogSize; + + this.attribute_map = { + allowEmptySignatures: 'allow-empty-signatures', + extraOpcodeBudget: 'extra-opcode-budget', + maxLogCalls: 'max-log-calls', + maxLogSize: 'max-log-size', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): SimulationEvalOverrides { + /* eslint-disable dot-notation */ + return new SimulationEvalOverrides({ + allowEmptySignatures: data['allow-empty-signatures'], + extraOpcodeBudget: data['extra-opcode-budget'], + maxLogCalls: data['max-log-calls'], + maxLogSize: data['max-log-size'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Represents a state proof and its corresponding message + */ +export class StateProof extends BaseModel { + /** + * Represents the message that the state proofs are attesting to. + */ + public message: StateProofMessage; + + /** + * The encoded StateProof for the message. + */ + public stateproof: Uint8Array; + + /** + * Creates a new `StateProof` object. + * @param message - Represents the message that the state proofs are attesting to. + * @param stateproof - The encoded StateProof for the message. + */ + constructor({ + message, + stateproof, + }: { + message: StateProofMessage; + stateproof: string | Uint8Array; + }) { + super(); + this.message = message; + this.stateproof = + typeof stateproof === 'string' + ? new Uint8Array(Buffer.from(stateproof, 'base64')) + : stateproof; + + this.attribute_map = { + message: 'Message', + stateproof: 'StateProof', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): StateProof { + /* eslint-disable dot-notation */ + if (typeof data['Message'] === 'undefined') + throw new Error(`Response is missing required field 'Message': ${data}`); + if (typeof data['StateProof'] === 'undefined') + throw new Error( + `Response is missing required field 'StateProof': ${data}` + ); + return new StateProof({ + message: StateProofMessage.from_obj_for_encoding(data['Message']), + stateproof: data['StateProof'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Represents the message that the state proofs are attesting to. + */ +export class StateProofMessage extends BaseModel { + /** + * The vector commitment root on all light block headers within a state proof + * interval. + */ + public blockheaderscommitment: Uint8Array; + + /** + * The first round the message attests to. + */ + public firstattestedround: number | bigint; + + /** + * The last round the message attests to. + */ + public lastattestedround: number | bigint; + + /** + * An integer value representing the natural log of the proven weight with 16 bits + * of precision. This value would be used to verify the next state proof. + */ + public lnprovenweight: number | bigint; + + /** + * The vector commitment root of the top N accounts to sign the next StateProof. + */ + public voterscommitment: Uint8Array; + + /** + * Creates a new `StateProofMessage` object. + * @param blockheaderscommitment - The vector commitment root on all light block headers within a state proof + * interval. + * @param firstattestedround - The first round the message attests to. + * @param lastattestedround - The last round the message attests to. + * @param lnprovenweight - An integer value representing the natural log of the proven weight with 16 bits + * of precision. This value would be used to verify the next state proof. + * @param voterscommitment - The vector commitment root of the top N accounts to sign the next StateProof. + */ + constructor({ + blockheaderscommitment, + firstattestedround, + lastattestedround, + lnprovenweight, + voterscommitment, + }: { + blockheaderscommitment: string | Uint8Array; + firstattestedround: number | bigint; + lastattestedround: number | bigint; + lnprovenweight: number | bigint; + voterscommitment: string | Uint8Array; + }) { + super(); + this.blockheaderscommitment = + typeof blockheaderscommitment === 'string' + ? new Uint8Array(Buffer.from(blockheaderscommitment, 'base64')) + : blockheaderscommitment; + this.firstattestedround = firstattestedround; + this.lastattestedround = lastattestedround; + this.lnprovenweight = lnprovenweight; + this.voterscommitment = + typeof voterscommitment === 'string' + ? new Uint8Array(Buffer.from(voterscommitment, 'base64')) + : voterscommitment; + + this.attribute_map = { + blockheaderscommitment: 'BlockHeadersCommitment', + firstattestedround: 'FirstAttestedRound', + lastattestedround: 'LastAttestedRound', + lnprovenweight: 'LnProvenWeight', + voterscommitment: 'VotersCommitment', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): StateProofMessage { + /* eslint-disable dot-notation */ + if (typeof data['BlockHeadersCommitment'] === 'undefined') + throw new Error( + `Response is missing required field 'BlockHeadersCommitment': ${data}` + ); + if (typeof data['FirstAttestedRound'] === 'undefined') + throw new Error( + `Response is missing required field 'FirstAttestedRound': ${data}` + ); + if (typeof data['LastAttestedRound'] === 'undefined') + throw new Error( + `Response is missing required field 'LastAttestedRound': ${data}` + ); + if (typeof data['LnProvenWeight'] === 'undefined') + throw new Error( + `Response is missing required field 'LnProvenWeight': ${data}` + ); + if (typeof data['VotersCommitment'] === 'undefined') + throw new Error( + `Response is missing required field 'VotersCommitment': ${data}` + ); + return new StateProofMessage({ + blockheaderscommitment: data['BlockHeadersCommitment'], + firstattestedround: data['FirstAttestedRound'], + lastattestedround: data['LastAttestedRound'], + lnprovenweight: data['LnProvenWeight'], + voterscommitment: data['VotersCommitment'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Supply represents the current supply of MicroAlgos in the system. + */ +export class SupplyResponse extends BaseModel { + /** + * Round + */ + public currentRound: number | bigint; + + /** + * OnlineMoney + */ + public onlineMoney: number | bigint; + + /** + * TotalMoney + */ + public totalMoney: number | bigint; + + /** + * Creates a new `SupplyResponse` object. + * @param currentRound - Round + * @param onlineMoney - OnlineMoney + * @param totalMoney - TotalMoney + */ + constructor({ + currentRound, + onlineMoney, + totalMoney, + }: { + currentRound: number | bigint; + onlineMoney: number | bigint; + totalMoney: number | bigint; + }) { + super(); + this.currentRound = currentRound; + this.onlineMoney = onlineMoney; + this.totalMoney = totalMoney; + + this.attribute_map = { + currentRound: 'current_round', + onlineMoney: 'online-money', + totalMoney: 'total-money', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): SupplyResponse { + /* eslint-disable dot-notation */ + if (typeof data['current_round'] === 'undefined') + throw new Error( + `Response is missing required field 'current_round': ${data}` + ); + if (typeof data['online-money'] === 'undefined') + throw new Error( + `Response is missing required field 'online-money': ${data}` + ); + if (typeof data['total-money'] === 'undefined') + throw new Error( + `Response is missing required field 'total-money': ${data}` + ); + return new SupplyResponse({ + currentRound: data['current_round'], + onlineMoney: data['online-money'], + totalMoney: data['total-money'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Represents a key-value pair in an application store. + */ +export class TealKeyValue extends BaseModel { + public key: string; + + /** + * Represents a TEAL value. + */ + public value: TealValue; + + /** + * Creates a new `TealKeyValue` object. + * @param key - + * @param value - Represents a TEAL value. + */ + constructor({ key, value }: { key: string; value: TealValue }) { + super(); + this.key = key; + this.value = value; + + this.attribute_map = { + key: 'key', + value: 'value', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): TealKeyValue { + /* eslint-disable dot-notation */ + if (typeof data['key'] === 'undefined') + throw new Error(`Response is missing required field 'key': ${data}`); + if (typeof data['value'] === 'undefined') + throw new Error(`Response is missing required field 'value': ${data}`); + return new TealKeyValue({ + key: data['key'], + value: TealValue.from_obj_for_encoding(data['value']), + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Represents a TEAL value. + */ +export class TealValue extends BaseModel { + /** + * (tt) value type. Value `1` refers to **bytes**, value `2` refers to **uint** + */ + public type: number | bigint; + + /** + * (tb) bytes value. + */ + public bytes: string; + + /** + * (ui) uint value. + */ + public uint: number | bigint; + + /** + * Creates a new `TealValue` object. + * @param type - (tt) value type. Value `1` refers to **bytes**, value `2` refers to **uint** + * @param bytes - (tb) bytes value. + * @param uint - (ui) uint value. + */ + constructor({ + type, + bytes, + uint, + }: { + type: number | bigint; + bytes: string; + uint: number | bigint; + }) { + super(); + this.type = type; + this.bytes = bytes; + this.uint = uint; + + this.attribute_map = { + type: 'type', + bytes: 'bytes', + uint: 'uint', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): TealValue { + /* eslint-disable dot-notation */ + if (typeof data['type'] === 'undefined') + throw new Error(`Response is missing required field 'type': ${data}`); + if (typeof data['bytes'] === 'undefined') + throw new Error(`Response is missing required field 'bytes': ${data}`); + if (typeof data['uint'] === 'undefined') + throw new Error(`Response is missing required field 'uint': ${data}`); + return new TealValue({ + type: data['type'], + bytes: data['bytes'], + uint: data['uint'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Response containing all ledger state deltas for transaction groups, with their + * associated Ids, in a single round. + */ +export class TransactionGroupLedgerStateDeltasForRoundResponse extends BaseModel { + public deltas: LedgerStateDeltaForTransactionGroup[]; + + /** + * Creates a new `TransactionGroupLedgerStateDeltasForRoundResponse` object. + * @param deltas - + */ + constructor({ deltas }: { deltas: LedgerStateDeltaForTransactionGroup[] }) { + super(); + this.deltas = deltas; + + this.attribute_map = { + deltas: 'deltas', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionGroupLedgerStateDeltasForRoundResponse { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['Deltas'])) + throw new Error( + `Response is missing required array field 'Deltas': ${data}` + ); + return new TransactionGroupLedgerStateDeltasForRoundResponse({ + deltas: data['Deltas'].map( + LedgerStateDeltaForTransactionGroup.from_obj_for_encoding + ), + }); + /* eslint-enable dot-notation */ + } +} + +/** + * TransactionParams contains the parameters that help a client construct a new + * transaction. + */ +export class TransactionParametersResponse extends BaseModel { + /** + * ConsensusVersion indicates the consensus protocol version + * as of LastRound. + */ + public consensusVersion: string; + + /** + * Fee is the suggested transaction fee + * Fee is in units of micro-Algos per byte. + * Fee may fall to zero but transactions must still have a fee of + * at least MinTxnFee for the current network protocol. + */ + public fee: number | bigint; + + /** + * GenesisHash is the hash of the genesis block. + */ + public genesisHash: Uint8Array; + + /** + * GenesisID is an ID listed in the genesis block. + */ + public genesisId: string; + + /** + * LastRound indicates the last round seen + */ + public lastRound: number | bigint; + + /** + * The minimum transaction fee (not per byte) required for the + * txn to validate for the current network protocol. + */ + public minFee: number | bigint; + + /** + * Creates a new `TransactionParametersResponse` object. + * @param consensusVersion - ConsensusVersion indicates the consensus protocol version + * as of LastRound. + * @param fee - Fee is the suggested transaction fee + * Fee is in units of micro-Algos per byte. + * Fee may fall to zero but transactions must still have a fee of + * at least MinTxnFee for the current network protocol. + * @param genesisHash - GenesisHash is the hash of the genesis block. + * @param genesisId - GenesisID is an ID listed in the genesis block. + * @param lastRound - LastRound indicates the last round seen + * @param minFee - The minimum transaction fee (not per byte) required for the + * txn to validate for the current network protocol. + */ + constructor({ + consensusVersion, + fee, + genesisHash, + genesisId, + lastRound, + minFee, + }: { + consensusVersion: string; + fee: number | bigint; + genesisHash: string | Uint8Array; + genesisId: string; + lastRound: number | bigint; + minFee: number | bigint; + }) { + super(); + this.consensusVersion = consensusVersion; + this.fee = fee; + this.genesisHash = + typeof genesisHash === 'string' + ? new Uint8Array(Buffer.from(genesisHash, 'base64')) + : genesisHash; + this.genesisId = genesisId; + this.lastRound = lastRound; + this.minFee = minFee; + + this.attribute_map = { + consensusVersion: 'consensus-version', + fee: 'fee', + genesisHash: 'genesis-hash', + genesisId: 'genesis-id', + lastRound: 'last-round', + minFee: 'min-fee', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionParametersResponse { + /* eslint-disable dot-notation */ + if (typeof data['consensus-version'] === 'undefined') + throw new Error( + `Response is missing required field 'consensus-version': ${data}` + ); + if (typeof data['fee'] === 'undefined') + throw new Error(`Response is missing required field 'fee': ${data}`); + if (typeof data['genesis-hash'] === 'undefined') + throw new Error( + `Response is missing required field 'genesis-hash': ${data}` + ); + if (typeof data['genesis-id'] === 'undefined') + throw new Error( + `Response is missing required field 'genesis-id': ${data}` + ); + if (typeof data['last-round'] === 'undefined') + throw new Error( + `Response is missing required field 'last-round': ${data}` + ); + if (typeof data['min-fee'] === 'undefined') + throw new Error(`Response is missing required field 'min-fee': ${data}`); + return new TransactionParametersResponse({ + consensusVersion: data['consensus-version'], + fee: data['fee'], + genesisHash: data['genesis-hash'], + genesisId: data['genesis-id'], + lastRound: data['last-round'], + minFee: data['min-fee'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Proof of transaction in a block. + */ +export class TransactionProofResponse extends BaseModel { + /** + * Index of the transaction in the block's payset. + */ + public idx: number | bigint; + + /** + * Proof of transaction membership. + */ + public proof: Uint8Array; + + /** + * Hash of SignedTxnInBlock for verifying proof. + */ + public stibhash: Uint8Array; + + /** + * Represents the depth of the tree that is being proven, i.e. the number of edges + * from a leaf to the root. + */ + public treedepth: number | bigint; + + /** + * The type of hash function used to create the proof, must be one of: + * * sha512_256 + * * sha256 + */ + public hashtype?: string; + + /** + * Creates a new `TransactionProofResponse` object. + * @param idx - Index of the transaction in the block's payset. + * @param proof - Proof of transaction membership. + * @param stibhash - Hash of SignedTxnInBlock for verifying proof. + * @param treedepth - Represents the depth of the tree that is being proven, i.e. the number of edges + * from a leaf to the root. + * @param hashtype - The type of hash function used to create the proof, must be one of: + * * sha512_256 + * * sha256 + */ + constructor({ + idx, + proof, + stibhash, + treedepth, + hashtype, + }: { + idx: number | bigint; + proof: string | Uint8Array; + stibhash: string | Uint8Array; + treedepth: number | bigint; + hashtype?: string; + }) { + super(); + this.idx = idx; + this.proof = + typeof proof === 'string' + ? new Uint8Array(Buffer.from(proof, 'base64')) + : proof; + this.stibhash = + typeof stibhash === 'string' + ? new Uint8Array(Buffer.from(stibhash, 'base64')) + : stibhash; + this.treedepth = treedepth; + this.hashtype = hashtype; + + this.attribute_map = { + idx: 'idx', + proof: 'proof', + stibhash: 'stibhash', + treedepth: 'treedepth', + hashtype: 'hashtype', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionProofResponse { + /* eslint-disable dot-notation */ + if (typeof data['idx'] === 'undefined') + throw new Error(`Response is missing required field 'idx': ${data}`); + if (typeof data['proof'] === 'undefined') + throw new Error(`Response is missing required field 'proof': ${data}`); + if (typeof data['stibhash'] === 'undefined') + throw new Error(`Response is missing required field 'stibhash': ${data}`); + if (typeof data['treedepth'] === 'undefined') + throw new Error( + `Response is missing required field 'treedepth': ${data}` + ); + return new TransactionProofResponse({ + idx: data['idx'], + proof: data['proof'], + stibhash: data['stibhash'], + treedepth: data['treedepth'], + hashtype: data['hashtype'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * algod version information. + */ +export class Version extends BaseModel { + public build: BuildVersion; + + public genesisHashB64: Uint8Array; + + public genesisId: string; + + public versions: string[]; + + /** + * Creates a new `Version` object. + * @param build - + * @param genesisHashB64 - + * @param genesisId - + * @param versions - + */ + constructor({ + build, + genesisHashB64, + genesisId, + versions, + }: { + build: BuildVersion; + genesisHashB64: string | Uint8Array; + genesisId: string; + versions: string[]; + }) { + super(); + this.build = build; + this.genesisHashB64 = + typeof genesisHashB64 === 'string' + ? new Uint8Array(Buffer.from(genesisHashB64, 'base64')) + : genesisHashB64; + this.genesisId = genesisId; + this.versions = versions; + + this.attribute_map = { + build: 'build', + genesisHashB64: 'genesis_hash_b64', + genesisId: 'genesis_id', + versions: 'versions', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): Version { + /* eslint-disable dot-notation */ + if (typeof data['build'] === 'undefined') + throw new Error(`Response is missing required field 'build': ${data}`); + if (typeof data['genesis_hash_b64'] === 'undefined') + throw new Error( + `Response is missing required field 'genesis_hash_b64': ${data}` + ); + if (typeof data['genesis_id'] === 'undefined') + throw new Error( + `Response is missing required field 'genesis_id': ${data}` + ); + if (!Array.isArray(data['versions'])) + throw new Error( + `Response is missing required array field 'versions': ${data}` + ); + return new Version({ + build: BuildVersion.from_obj_for_encoding(data['build']), + genesisHashB64: data['genesis_hash_b64'], + genesisId: data['genesis_id'], + versions: data['versions'], + }); + /* eslint-enable dot-notation */ + } +} diff --git a/src/client/v2/algod/pendingTransactionInformation.ts b/src/client/v2/algod/pendingTransactionInformation.ts new file mode 100644 index 0000000..75d2527 --- /dev/null +++ b/src/client/v2/algod/pendingTransactionInformation.ts @@ -0,0 +1,32 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import * as encoding from '../../../encoding/encoding'; + +/** + * returns the transaction information for a specific txid of a pending transaction + */ +export default class PendingTransactionInformation extends JSONRequest { + constructor(c: HTTPClient, private txid: string) { + super(c); + this.txid = txid; + this.query.format = 'msgpack'; + } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Uint8Array) { + if (body && body.byteLength > 0) { + return encoding.decode(body) as Record; + } + return undefined; + } + + path() { + return `/v2/transactions/pending/${this.txid}`; + } + + // max sets the maximum number of txs to return + max(max: number) { + this.query.max = max; + return this; + } +} diff --git a/src/client/v2/algod/pendingTransactions.ts b/src/client/v2/algod/pendingTransactions.ts new file mode 100644 index 0000000..bf87548 --- /dev/null +++ b/src/client/v2/algod/pendingTransactions.ts @@ -0,0 +1,32 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import * as encoding from '../../../encoding/encoding'; + +/** + * pendingTransactionsInformation returns transactions that are pending in the pool + */ +export default class PendingTransactions extends JSONRequest { + constructor(c: HTTPClient) { + super(c); + this.query.format = 'msgpack'; + } + + /* eslint-disable class-methods-use-this */ + path() { + return '/v2/transactions/pending'; + } + + prepare(body: Uint8Array) { + if (body && body.byteLength > 0) { + return encoding.decode(body) as Record; + } + return undefined; + } + /* eslint-enable class-methods-use-this */ + + // max sets the maximum number of txs to return + max(max: number) { + this.query.max = max; + return this; + } +} diff --git a/src/client/v2/algod/pendingTransactionsByAddress.ts b/src/client/v2/algod/pendingTransactionsByAddress.ts new file mode 100644 index 0000000..b9710cc --- /dev/null +++ b/src/client/v2/algod/pendingTransactionsByAddress.ts @@ -0,0 +1,32 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import * as encoding from '../../../encoding/encoding'; + +/** + * returns all transactions for a PK [addr] in the [first, last] rounds range. + */ +export default class PendingTransactionsByAddress extends JSONRequest { + constructor(c: HTTPClient, private address: string) { + super(c); + this.address = address; + this.query.format = 'msgpack'; + } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Uint8Array): Record { + if (body && body.byteLength > 0) { + return encoding.decode(body) as Record; + } + return undefined; + } + + path() { + return `/v2/accounts/${this.address}/transactions/pending`; + } + + // max sets the maximum number of txs to return + max(max: number) { + this.query.max = max; + return this; + } +} diff --git a/src/client/v2/algod/ready.ts b/src/client/v2/algod/ready.ts new file mode 100644 index 0000000..32c0329 --- /dev/null +++ b/src/client/v2/algod/ready.ts @@ -0,0 +1,8 @@ +import JSONRequest from '../jsonrequest'; + +export default class Ready extends JSONRequest { + // eslint-disable-next-line class-methods-use-this + path() { + return `/ready`; + } +} diff --git a/src/client/v2/algod/sendRawTransaction.ts b/src/client/v2/algod/sendRawTransaction.ts new file mode 100644 index 0000000..8977c1c --- /dev/null +++ b/src/client/v2/algod/sendRawTransaction.ts @@ -0,0 +1,60 @@ +import { Buffer } from 'buffer'; +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import { concatArrays } from '../../../utils/utils'; + +/** + * Sets the default header (if not previously set) for sending a raw + * transaction. + * @param headers - A headers object + */ +export function setSendTransactionHeaders(headers = {}) { + let hdrs = headers; + if (Object.keys(hdrs).every((key) => key.toLowerCase() !== 'content-type')) { + hdrs = { ...headers }; + hdrs['Content-Type'] = 'application/x-binary'; + } + return hdrs; +} + +function isByteArray(array: any): array is Uint8Array { + return array && array.byteLength !== undefined; +} + +/** + * broadcasts the passed signed txns to the network + */ +export default class SendRawTransaction extends JSONRequest { + private txnBytesToPost: Uint8Array; + + constructor(c: HTTPClient, stxOrStxs: Uint8Array | Uint8Array[]) { + super(c); + + let forPosting = stxOrStxs; + if (Array.isArray(stxOrStxs)) { + if (!stxOrStxs.every(isByteArray)) { + throw new TypeError('Array elements must be byte arrays'); + } + // Flatten into a single Uint8Array + forPosting = concatArrays(...stxOrStxs); + } else if (!isByteArray(forPosting)) { + throw new TypeError('Argument must be byte array'); + } + this.txnBytesToPost = forPosting; + } + + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/transactions'; + } + + async do(headers = {}) { + const txHeaders = setSendTransactionHeaders(headers); + const res = await this.c.post( + this.path(), + Buffer.from(this.txnBytesToPost), + txHeaders + ); + return res.body; + } +} diff --git a/src/client/v2/algod/setBlockOffsetTimestamp.ts b/src/client/v2/algod/setBlockOffsetTimestamp.ts new file mode 100644 index 0000000..8402281 --- /dev/null +++ b/src/client/v2/algod/setBlockOffsetTimestamp.ts @@ -0,0 +1,20 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class SetBlockOffsetTimestamp extends JSONRequest { + constructor(c: HTTPClient, intDecoding: IntDecoding, private offset: number) { + super(c, intDecoding); + + this.offset = offset; + } + + path() { + return `/v2/devmode/blocks/offset/${this.offset}`; + } + + async do(headers = {}) { + const res = await this.c.post(this.path(), headers); + return res.body; + } +} diff --git a/src/client/v2/algod/setSyncRound.ts b/src/client/v2/algod/setSyncRound.ts new file mode 100644 index 0000000..fdc32c7 --- /dev/null +++ b/src/client/v2/algod/setSyncRound.ts @@ -0,0 +1,20 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class SetSyncRound extends JSONRequest { + constructor(c: HTTPClient, intDecoding: IntDecoding, private round: number) { + super(c, intDecoding); + + this.round = round; + } + + path() { + return `/v2/ledger/sync/${this.round}`; + } + + async do(headers = {}) { + const res = await this.c.post(this.path(), headers); + return res.body; + } +} diff --git a/src/client/v2/algod/simulateTransaction.ts b/src/client/v2/algod/simulateTransaction.ts new file mode 100644 index 0000000..c6dad54 --- /dev/null +++ b/src/client/v2/algod/simulateTransaction.ts @@ -0,0 +1,58 @@ +import { Buffer } from 'buffer'; +import * as encoding from '../../../encoding/encoding'; +import HTTPClient from '../../client'; +import JSONRequest from '../jsonrequest'; +import { SimulateRequest, SimulateResponse } from './models/types'; + +/** + * Sets the default header (if not previously set) for simulating a raw + * transaction. + * @param headers - A headers object + */ +export function setSimulateTransactionsHeaders(headers = {}) { + let hdrs = headers; + if (Object.keys(hdrs).every((key) => key.toLowerCase() !== 'content-type')) { + hdrs = { ...headers }; + hdrs['Content-Type'] = 'application/msgpack'; + } + return hdrs; +} + +/** + * Simulates signed txns. + */ +export default class SimulateRawTransactions extends JSONRequest< + SimulateResponse, + Uint8Array +> { + private requestBytes: Uint8Array; + + constructor(c: HTTPClient, request: SimulateRequest) { + super(c); + this.query.format = 'msgpack'; + this.requestBytes = encoding.rawEncode(request.get_obj_for_encoding(true)); + } + + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/transactions/simulate'; + } + + async do(headers = {}) { + const txHeaders = setSimulateTransactionsHeaders(headers); + const res = await this.c.post( + this.path(), + Buffer.from(this.requestBytes), + txHeaders, + this.query, + false + ); + return this.prepare(res.body); + } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Uint8Array): SimulateResponse { + const decoded = encoding.decode(body); + return SimulateResponse.from_obj_for_encoding(decoded); + } +} diff --git a/src/client/v2/algod/stateproof.ts b/src/client/v2/algod/stateproof.ts new file mode 100644 index 0000000..98bab12 --- /dev/null +++ b/src/client/v2/algod/stateproof.ts @@ -0,0 +1,15 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class StateProof extends JSONRequest { + constructor(c: HTTPClient, intDecoding: IntDecoding, private round: number) { + super(c, intDecoding); + + this.round = round; + } + + path() { + return `/v2/stateproofs/${this.round}`; + } +} diff --git a/src/client/v2/algod/status.ts b/src/client/v2/algod/status.ts new file mode 100644 index 0000000..c8dcd45 --- /dev/null +++ b/src/client/v2/algod/status.ts @@ -0,0 +1,8 @@ +import JSONRequest from '../jsonrequest'; + +export default class Status extends JSONRequest { + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/status'; + } +} diff --git a/src/client/v2/algod/statusAfterBlock.ts b/src/client/v2/algod/statusAfterBlock.ts new file mode 100644 index 0000000..5d340c6 --- /dev/null +++ b/src/client/v2/algod/statusAfterBlock.ts @@ -0,0 +1,15 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class StatusAfterBlock extends JSONRequest { + constructor(c: HTTPClient, intDecoding: IntDecoding, private round: number) { + super(c, intDecoding); + if (!Number.isInteger(round)) throw Error('round should be an integer'); + this.round = round; + } + + path() { + return `/v2/status/wait-for-block-after/${this.round}`; + } +} diff --git a/src/client/v2/algod/suggestedParams.ts b/src/client/v2/algod/suggestedParams.ts new file mode 100644 index 0000000..86421a2 --- /dev/null +++ b/src/client/v2/algod/suggestedParams.ts @@ -0,0 +1,25 @@ +import JSONRequest from '../jsonrequest'; +import { SuggestedParamsWithMinFee } from '../../../types/transactions/base'; + +/** + * Returns the common needed parameters for a new transaction, in a format the transaction builder expects + */ +export default class SuggestedParamsRequest extends JSONRequest { + /* eslint-disable class-methods-use-this */ + path() { + return '/v2/transactions/params'; + } + + prepare(body: Record): SuggestedParamsWithMinFee { + return { + flatFee: false, + fee: body.fee, + firstRound: body['last-round'], + lastRound: body['last-round'] + 1000, + genesisID: body['genesis-id'], + genesisHash: body['genesis-hash'], + minFee: body['min-fee'], + }; + } + /* eslint-enable class-methods-use-this */ +} diff --git a/src/client/v2/algod/supply.ts b/src/client/v2/algod/supply.ts new file mode 100644 index 0000000..0e30f56 --- /dev/null +++ b/src/client/v2/algod/supply.ts @@ -0,0 +1,8 @@ +import JSONRequest from '../jsonrequest'; + +export default class Supply extends JSONRequest { + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/ledger/supply'; + } +} diff --git a/src/client/v2/algod/unsetSyncRound.ts b/src/client/v2/algod/unsetSyncRound.ts new file mode 100644 index 0000000..746dbe5 --- /dev/null +++ b/src/client/v2/algod/unsetSyncRound.ts @@ -0,0 +1,13 @@ +import JSONRequest from '../jsonrequest'; + +export default class UnsetSyncRound extends JSONRequest { + // eslint-disable-next-line class-methods-use-this + path() { + return `/v2/ledger/sync`; + } + + async do(headers = {}) { + const res = await this.c.delete(this.path(), headers); + return res.body; + } +} diff --git a/src/client/v2/algod/versions.ts b/src/client/v2/algod/versions.ts new file mode 100644 index 0000000..31bcabb --- /dev/null +++ b/src/client/v2/algod/versions.ts @@ -0,0 +1,11 @@ +import JSONRequest from '../jsonrequest'; + +/** + * retrieves the VersionResponse from the running node + */ +export default class Versions extends JSONRequest { + // eslint-disable-next-line class-methods-use-this + path() { + return '/versions'; + } +} diff --git a/src/client/v2/basemodel.ts b/src/client/v2/basemodel.ts new file mode 100644 index 0000000..39fa52e --- /dev/null +++ b/src/client/v2/basemodel.ts @@ -0,0 +1,81 @@ +import { Buffer } from 'buffer'; + +/** + * Base class for models + */ + +/* eslint-disable no-underscore-dangle,camelcase */ +function _is_primitive(val: any): val is string | boolean | number | bigint { + /* eslint-enable no-underscore-dangle,camelcase */ + return ( + val === undefined || + val == null || + (typeof val !== 'object' && typeof val !== 'function') + ); +} + +/* eslint-disable no-underscore-dangle,camelcase,no-redeclare,no-unused-vars */ +function _get_obj_for_encoding( + val: Function, + binary: boolean +): Record; +function _get_obj_for_encoding(val: any[], binary: boolean): any[]; +function _get_obj_for_encoding( + val: Record, + binary: boolean +): Record; +function _get_obj_for_encoding(val: any, binary: boolean): any { + /* eslint-enable no-underscore-dangle,camelcase,no-redeclare,no-unused-vars */ + let targetPropValue: any; + + if (val instanceof Uint8Array) { + targetPropValue = binary ? val : Buffer.from(val).toString('base64'); + } else if (typeof val.get_obj_for_encoding === 'function') { + targetPropValue = val.get_obj_for_encoding(binary); + } else if (Array.isArray(val)) { + targetPropValue = []; + for (const elem of val) { + targetPropValue.push(_get_obj_for_encoding(elem, binary)); + } + } else if (typeof val === 'object') { + const obj = {}; + for (const prop of Object.keys(val)) { + obj[prop] = _get_obj_for_encoding(val[prop], binary); + } + targetPropValue = obj; + } else if (_is_primitive(val)) { + targetPropValue = val; + } else { + throw new Error(`Unsupported value: ${String(val)}`); + } + return targetPropValue; +} + +export default class BaseModel { + /* eslint-disable no-underscore-dangle,camelcase */ + attribute_map: Record; + + /** + * Get an object ready for encoding to either JSON or msgpack. + * @param binary - Use true to indicate that the encoding can handle raw binary objects + * (Uint8Arrays). Use false to indicate that raw binary objects should be converted to base64 + * strings. True should be used for objects that will be encoded with msgpack, and false should + * be used for objects that will be encoded with JSON. + */ + get_obj_for_encoding(binary = false) { + /* eslint-enable no-underscore-dangle,camelcase */ + const obj: Record = {}; + + for (const prop of Object.keys(this.attribute_map)) { + const name = this.attribute_map[prop]; + const value = this[prop]; + + if (typeof value !== 'undefined') { + obj[name] = + value === null ? null : _get_obj_for_encoding(value, binary); + } + } + + return obj; + } +} diff --git a/src/client/v2/indexer/indexer.ts b/src/client/v2/indexer/indexer.ts new file mode 100644 index 0000000..b767343 --- /dev/null +++ b/src/client/v2/indexer/indexer.ts @@ -0,0 +1,432 @@ +import ServiceClient from '../serviceClient'; +import MakeHealthCheck from './makeHealthCheck'; +import LookupAssetBalances from './lookupAssetBalances'; +import LookupAssetTransactions from './lookupAssetTransactions'; +import LookupAccountTransactions from './lookupAccountTransactions'; +import LookupBlock from './lookupBlock'; +import LookupTransactionByID from './lookupTransactionByID'; +import LookupAccountByID from './lookupAccountByID'; +import LookupAccountAssets from './lookupAccountAssets'; +import LookupAccountCreatedAssets from './lookupAccountCreatedAssets'; +import LookupAccountAppLocalStates from './lookupAccountAppLocalStates'; +import LookupAccountCreatedApplications from './lookupAccountCreatedApplications'; +import LookupAssetByID from './lookupAssetByID'; +import LookupApplications from './lookupApplications'; +import LookupApplicationLogs from './lookupApplicationLogs'; +import LookupApplicationBoxByIDandName from './lookupApplicationBoxByIDandName'; +import SearchAccounts from './searchAccounts'; +import SearchForTransactions from './searchForTransactions'; +import SearchForAssets from './searchForAssets'; +import SearchForApplications from './searchForApplications'; +import SearchForApplicationBoxes from './searchForApplicationBoxes'; +import { BaseHTTPClient } from '../../baseHTTPClient'; +import { + CustomTokenHeader, + IndexerTokenHeader, +} from '../../urlTokenBaseHTTPClient'; + +/** + * The Indexer provides a REST API interface of API calls to support searching the Algorand Blockchain. + * + * The Indexer REST APIs retrieve the blockchain data from a PostgreSQL compatible database that must be populated. + * + * This database is populated using the same indexer instance or a separate instance of the indexer which must connect to the algod process of a running Algorand node to read block data. + * + * This node must also be an Archival node to make searching the entire blockchain possible. + * + * #### Relevant Information + * [Learn more about Indexer](https://developer.algorand.org/docs/get-details/indexer/) + * + * [Run Indexer in Postman OAS3](https://developer.algorand.org/docs/rest-apis/restendpoints/#algod-indexer-and-kmd-rest-endpoints) + */ +export default class IndexerClient extends ServiceClient { + /** + * Create an IndexerClient from + * * either a token, baseServer, port, and optional headers + * * or a base client server for interoperability with external dApp wallets + * + * #### Example + * ```typescript + * const token = ""; + * const server = "http://localhost"; + * const port = 8980; + * const indexerClient = new algosdk.Indexer(token, server, port); + * ``` + * @remarks + * The above configuration is for a sandbox private network. + * For applications on production, you are encouraged to run your own node with indexer, or use an Algorand REST API provider with a dedicated API key. + * + * @param tokenOrBaseClient - The API token for the Indexer API + * @param baseServer - REST endpoint + * @param port - Port number if specifically configured by the server + * @param headers - Optional headers + */ + constructor( + tokenOrBaseClient: + | string + | IndexerTokenHeader + | CustomTokenHeader + | BaseHTTPClient, + baseServer = 'http://127.0.0.1', + port: string | number = 8080, + headers: Record = {} + ) { + super('X-Indexer-API-Token', tokenOrBaseClient, baseServer, port, headers); + } + + /** + * Returns the health object for the service. + * Returns 200 if healthy. + * + * #### Example + * ```typescript + * const health = await indexerClient.makeHealthCheck().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-health) + * @category GET + */ + makeHealthCheck() { + return new MakeHealthCheck(this.c, this.intDecoding); + } + + /** + * Returns the list of accounts who hold the given asset and their balance. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetBalances = await indexerClient.lookupAssetBalances(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-idbalances) + * @param index - The asset ID to look up. + * @category GET + */ + lookupAssetBalances(index: number) { + return new LookupAssetBalances(this.c, this.intDecoding, index); + } + + /** + * Returns transactions relating to the given asset. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient.lookupAssetTransactions(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-idtransactions) + * @param index - The asset ID to look up. + * @category GET + */ + lookupAssetTransactions(index: number) { + return new LookupAssetTransactions(this.c, this.intDecoding, index); + } + + /** + * Returns transactions relating to the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient.lookupAccountTransactions(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idtransactions) + * @param account - The address of the account. + * @category GET + */ + lookupAccountTransactions(account: string) { + return new LookupAccountTransactions(this.c, this.intDecoding, account); + } + + /** + * Returns the block for the passed round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const blockInfo = await indexerClient.lookupBlock(targetBlock).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2blocksround-number) + * @param round - The number of the round to look up. + * @category GET + */ + lookupBlock(round: number) { + return new LookupBlock(this.c, this.intDecoding, round); + } + + /** + * Returns information about the given transaction. + * + * #### Example + * ```typescript + * const txnId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const txnInfo = await indexerClient.lookupTransactionByID(txnId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2transactionstxid) + * @param txID - The ID of the transaction to look up. + * @category GET + */ + lookupTransactionByID(txID: string) { + return new LookupTransactionByID(this.c, this.intDecoding, txID); + } + + /** + * Returns information about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient.lookupAccountByID(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-id) + * @param account - The address of the account to look up. + * @category GET + */ + lookupAccountByID(account: string) { + return new LookupAccountByID(this.c, this.intDecoding, account); + } + + /** + * Returns asset about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient.lookupAccountAssets(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idassets) + * @param account - The address of the account to look up. + * @category GET + */ + lookupAccountAssets(account: string) { + return new LookupAccountAssets(this.c, this.intDecoding, account); + } + + /** + * Returns asset information created by the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountCreatedAssets = await indexerClient.lookupAccountCreatedAssets(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idcreated-assets) + * @param account - The address of the account to look up. + * @category GET + */ + lookupAccountCreatedAssets(account: string) { + return new LookupAccountCreatedAssets(this.c, this.intDecoding, account); + } + + /** + * Returns application local state about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAppLocalStates = await indexerClient.lookupAccountAppLocalStates(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idapps-local-state) + * @param account - The address of the account to look up. + * @category GET + */ + lookupAccountAppLocalStates(account: string) { + return new LookupAccountAppLocalStates(this.c, this.intDecoding, account); + } + + /** + * Returns application information created by the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountCreatedApps = await indexerClient.lookupAccountCreatedApplications(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idcreated-applications) + * @param account - The address of the account to look up. + * @category GET + */ + lookupAccountCreatedApplications(account: string) { + return new LookupAccountCreatedApplications( + this.c, + this.intDecoding, + account + ); + } + + /** + * Returns information about the passed asset. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetInfo = await indexerClient.lookupAssetByID(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-id) + * @param index - The ID of the asset ot look up. + * @category GET + */ + lookupAssetByID(index: number) { + return new LookupAssetByID(this.c, this.intDecoding, index); + } + + /** + * Returns information about the passed application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const appInfo = await indexerClient.lookupApplications(appId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-id) + * @param index - The ID of the application to look up. + * @category GET + */ + lookupApplications(index: number) { + return new LookupApplications(this.c, this.intDecoding, index); + } + + /** + * Returns log messages generated by the passed in application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const appLogs = await indexerClient.lookupApplicationLogs(appId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idlogs) + * @param appID - The ID of the application which generated the logs. + * @category GET + */ + lookupApplicationLogs(appID: number) { + return new LookupApplicationLogs(this.c, this.intDecoding, appID); + } + + /** + * Returns information about indexed accounts. + * + * #### Example + * ```typescript + * const accounts = await indexerClient.searchAccounts().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accounts) + * @category GET + */ + searchAccounts() { + return new SearchAccounts(this.c, this.intDecoding); + } + + /** + * Returns information about indexed transactions. + * + * #### Example + * ```typescript + * const txns = await indexerClient.searchForTransactions().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2transactions) + * @category GET + */ + searchForTransactions() { + return new SearchForTransactions(this.c, this.intDecoding); + } + + /** + * Returns information about indexed assets. + * + * #### Example + * ```typescript + * const assets = await indexerClient.searchForAssets().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assets) + * @category GET + */ + searchForAssets() { + return new SearchForAssets(this.c, this.intDecoding); + } + + /** + * Returns information about indexed applications. + * + * #### Example + * ```typescript + * const apps = await indexerClient.searchForApplications().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applications) + * @category GET + */ + searchForApplications() { + return new SearchForApplications(this.c, this.intDecoding); + } + + /** + * Returns information about indexed application boxes. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const appID = 1234; + * + * const responsePage1 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .do(); + * const boxNamesPage1 = responsePage1.boxes.map(box => box.name); + * + * const responsePage2 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .nextToken(responsePage1.nextToken) + * .do(); + * const boxNamesPage2 = responsePage2.boxes.map(box => box.name); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idboxes) + * @param appID - The ID of the application with boxes. + * @category GET + */ + searchForApplicationBoxes(appID: number) { + return new SearchForApplicationBoxes(this.c, this.intDecoding, appID); + } + + /** + * Returns information about the application box given its name. + * + * #### Example + * ```typescript + * const boxName = Buffer.from("foo"); + * const boxResponse = await indexerClient + * .LookupApplicationBoxByIDandName(1234, boxName) + * .do(); + * const boxValue = boxResponse.value; + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idbox) + * @param appID - The ID of the application with boxes. + * @category GET + */ + lookupApplicationBoxByIDandName(appID: number, boxName: Uint8Array) { + return new LookupApplicationBoxByIDandName( + this.c, + this.intDecoding, + appID, + boxName + ); + } +} diff --git a/src/client/v2/indexer/lookupAccountAppLocalStates.ts b/src/client/v2/indexer/lookupAccountAppLocalStates.ts new file mode 100644 index 0000000..4a2c2da --- /dev/null +++ b/src/client/v2/indexer/lookupAccountAppLocalStates.ts @@ -0,0 +1,140 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LookupAccountAppLocalStates extends JSONRequest { + /** + * Returns application local state about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAppLocalStates = await indexerClient.lookupAccountAppLocalStates(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idapps-local-state) + * @param account - The address of the account to look up. + * @category GET + */ + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private account: string + ) { + super(c, intDecoding); + this.account = account; + } + + /** + * @returns `/v2/accounts/${account}/apps-local-state` + */ + path() { + return `/v2/accounts/${this.account}/apps-local-state`; + } + + /** + * Add a limit for filter. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * const accountAssets = await indexerClient + * .lookupAccountAppLocalStates(address) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Specify round to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const targetBlock = 18309917; + * const accountAssets = await indexerClient + * .lookupAccountAppLocalStates(address) + * .round(targetBlock) + * .do(); + * ``` + * @param round + * @category query + */ + round(round: number) { + this.query.round = round; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * + * const accountAssetsPage1 = await indexerClient + * .lookupAccountAppLocalStates(address) + * .limit(maxResults) + * .do(); + * + * const accountAssetsPage2 = await indexerClient + * .lookupAccountAppLocalStates(address) + * .limit(maxResults) + * .next(accountAssetsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient + * .lookupAccountAppLocalStates(address) + * .includeAll(false) + * .do(); + * ``` + * @param value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Specify an applicationID to search for. + * + * #### Example + * ```typescript + * const applicationID = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountApplications = await indexerClient + * .lookupAccountAppLocalStates(address) + * .applicationID(applicationID) + * .do(); + * ``` + * @param index - the applicationID + * @category query + */ + applicationID(index: number) { + this.query['application-id'] = index; + return this; + } +} diff --git a/src/client/v2/indexer/lookupAccountAssets.ts b/src/client/v2/indexer/lookupAccountAssets.ts new file mode 100644 index 0000000..2d6194f --- /dev/null +++ b/src/client/v2/indexer/lookupAccountAssets.ts @@ -0,0 +1,141 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LookupAccountAssets extends JSONRequest { + /** + * Returns asset about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient.lookupAccountAssets(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idassets) + * @param account - The address of the account to look up. + * @category GET + */ + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private account: string + ) { + super(c, intDecoding); + this.account = account; + } + + /** + * @returns `/v2/accounts/${account}/assets` + */ + path() { + return `/v2/accounts/${this.account}/assets`; + } + + /** + * Add a limit for filter. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * const accountAssets = await indexerClient + * .lookupAccountAssets(address) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Specify round to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const targetBlock = 18309917; + * const accountAssets = await indexerClient + * .lookupAccountAssets(address) + * .round(targetBlock) + * .do(); + * ``` + * @param round + * @category query + */ + round(round: number) { + this.query.round = round; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * + * const accountAssetsPage1 = await indexerClient + * .lookupAccountAssets(address) + * .limit(maxResults) + * .do(); + * + * const accountAssetsPage2 = await indexerClient + * .lookupAccountAssets(address) + * .limit(maxResults) + * .next(accountAssetsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient + * .lookupAccountAssets(address) + * .includeAll(false) + * .do(); + * ``` + * @param value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Specify an assetID to search for. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const assetAssets = await indexerClient + * .lookupAccountAssets(address) + * .assetId(assetId) + * .do(); + * ``` + * @param index - the assetID + * @category query + */ + assetId(index: number) { + this.query['asset-id'] = index; + return this; + } +} diff --git a/src/client/v2/indexer/lookupAccountByID.ts b/src/client/v2/indexer/lookupAccountByID.ts new file mode 100644 index 0000000..8c024ec --- /dev/null +++ b/src/client/v2/indexer/lookupAccountByID.ts @@ -0,0 +1,109 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LookupAccountByID extends JSONRequest { + /** + * Returns information about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient.lookupAccountByID(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-id) + * @param account - The address of the account to look up. + * @category GET + */ + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private account: string + ) { + super(c, intDecoding); + this.account = account; + } + + /** + * @returns `/v2/accounts/${account}` + */ + path() { + return `/v2/accounts/${this.account}`; + } + + /** + * Specify round to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const targetBlock = 18309917; + * const accountInfo = await indexerClient + * .lookupAccountByID(address) + * .round(targetBlock) + * .do(); + * ``` + * @param round + */ + round(round: number) { + this.query.round = round; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates. + * + * #### Example 1 + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient + * .lookupAccountByID(address) + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient + * .lookupAccountByID(address) + * .includeAll() + * .do(); + * ``` + * @param value + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Exclude additional items such as asset holdings, application local data stored for this account, asset parameters created by this account, and application parameters created by this account. + * + * #### Example 1 + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient + * .lookupAccountByID(address) + * .exclude("all") + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient + * .lookupAccountByID(address) + * .exclude("assets,created-assets") + * .do(); + * ``` + * @remarks By default, it behaves as exclude=none + * @param exclude - Array of `all`, `assets`, `created-assets`, `apps-local-state`, `created-apps`, `none` + * @category query + */ + exclude(exclude: string) { + this.query.exclude = exclude; + return this; + } +} diff --git a/src/client/v2/indexer/lookupAccountCreatedApplications.ts b/src/client/v2/indexer/lookupAccountCreatedApplications.ts new file mode 100644 index 0000000..187ab33 --- /dev/null +++ b/src/client/v2/indexer/lookupAccountCreatedApplications.ts @@ -0,0 +1,141 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LookupAccountCreatedApplications extends JSONRequest { + /** + * Returns application information created by the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountCreatedApps = await indexerClient.lookupAccountCreatedApplications(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idcreated-applications) + * @param account - The address of the account to look up. + * @category GET + */ + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private account: string + ) { + super(c, intDecoding); + this.account = account; + } + + /** + * @returns `/v2/accounts/${account}/created-applications` + */ + path() { + return `/v2/accounts/${this.account}/created-applications`; + } + + /** + * Add a limit for filter. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * const accountAssets = await indexerClient + * .lookupAccountCreatedApplications(address) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Specify round to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const targetBlock = 18309917; + * const accountAssets = await indexerClient + * .lookupAccountCreatedApplications(address) + * .round(targetBlock) + * .do(); + * ``` + * @param round + * @category query + */ + round(round: number) { + this.query.round = round; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * + * const accountAssetsPage1 = await indexerClient + * .lookupAccountCreatedApplications(address) + * .limit(maxResults) + * .do(); + * + * const accountAssetsPage2 = await indexerClient + * .lookupAccountCreatedApplications(address) + * .limit(maxResults) + * .next(accountAssetsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient + * .lookupAccountCreatedApplications(address) + * .includeAll(false) + * .do(); + * ``` + * @param value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Specify an applicationID to search for. + * + * #### Example + * ```typescript + * const applicationID = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountApplications = await indexerClient + * .lookupAccountAppLocalStates(address) + * .applicationID(applicationID) + * .do(); + * ``` + * @param index - the applicationID + * @category query + */ + applicationID(index: number) { + this.query['application-id'] = index; + return this; + } +} diff --git a/src/client/v2/indexer/lookupAccountCreatedAssets.ts b/src/client/v2/indexer/lookupAccountCreatedAssets.ts new file mode 100644 index 0000000..f64a4e1 --- /dev/null +++ b/src/client/v2/indexer/lookupAccountCreatedAssets.ts @@ -0,0 +1,142 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LookupAccountCreatedAssets extends JSONRequest { + /** + * Returns asset information created by the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountCreatedAssets = await indexerClient.lookupAccountCreatedAssets(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idcreated-assets) + * @param account - The address of the account to look up. + * @category GET + */ + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private account: string + ) { + super(c, intDecoding); + this.account = account; + } + + /** + * @returns `/v2/accounts/${account}/created-assets` + */ + path() { + return `/v2/accounts/${this.account}/created-assets`; + } + + /** + * Add a limit for filter. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * const accountAssets = await indexerClient + * .lookupAccountCreatedAssets(address) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Specify round to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const targetBlock = 18309917; + * const accountAssets = await indexerClient + * .lookupAccountCreatedAssets(address) + * .round(targetBlock) + * .do(); + * ``` + * @param round + * @category query + */ + round(round: number) { + this.query.round = round; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * + * const accountAssetsPage1 = await indexerClient + * .lookupAccountCreatedAssets(address) + * .limit(maxResults) + * .do(); + * ``` + * + * const accountAssetsPage2 = await indexerClient + * .lookupAccountCreatedAssets(address) + * .limit(maxResults) + * .next(accountAssetsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient + * .lookupAccountCreatedAssets(address) + * .includeAll(false) + * .do(); + * ``` + * @param value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Specify an assetID to search for. + * + * #### Example + * ```typescript + * const assetID = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const assetAssets = await indexerClient + * .lookupAccountCreatedAssets(address) + * .assetID(assetID) + * .do(); + * ``` + * @param index - the assetID + * @category query + */ + assetID(index: number) { + this.query['asset-id'] = index; + return this; + } +} diff --git a/src/client/v2/indexer/lookupAccountTransactions.ts b/src/client/v2/indexer/lookupAccountTransactions.ts new file mode 100644 index 0000000..12f48fa --- /dev/null +++ b/src/client/v2/indexer/lookupAccountTransactions.ts @@ -0,0 +1,394 @@ +import { Buffer } from 'buffer'; +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +/** + * Accept base64 string or Uint8Array and output base64 string + * @param data - Base64 string or Uint8Array + * @returns The inputted base64 string, or a base64 string representation of the Uint8Array + */ +export function base64StringFunnel(data: Uint8Array | string) { + if (typeof data === 'string') { + return data; + } + return Buffer.from(data).toString('base64'); +} + +export default class LookupAccountTransactions extends JSONRequest { + /** + * Returns transactions relating to the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient.lookupAccountTransactions(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idtransactions) + * @param account - The address of the account. + */ + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private account: string + ) { + super(c, intDecoding); + this.account = account; + } + + /** + * @returns `/v2/accounts/${account}/transactions` + */ + path() { + return `/v2/accounts/${this.account}/transactions`; + } + + /** + * Specifies a prefix which must be contained in the note field. + * + * #### Example + * ```typescript + * const notePrefixBase64Encoded = "Y3JlYXRl"; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .notePrefix(notePrefixBase64Encoded) + * .do(); + * ``` + * + * @param prefix - base64 string or uint8array + * @category query + */ + notePrefix(prefix: Uint8Array | string) { + this.query['note-prefix'] = base64StringFunnel(prefix); + return this; + } + + /** + * Type of transaction to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .txType("appl") + * .do(); + * ``` + * + * @param type - one of `pay`, `keyreg`, `acfg`, `axfer`, `afrz`, `appl`, `stpf` + * @category query + */ + txType(type: string) { + this.query['tx-type'] = type; + return this; + } + + /** + * Type of signature to filter with. + * - sig: Standard + * - msig: MultiSig + * - lsig: LogicSig + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .sigType("msig") + * .do(); + * ``` + * + * @param type - one of `sig`, `msig`, `lsig` + * @category query + */ + sigType(type: string) { + this.query['sig-type'] = type; + return this; + } + + /** + * Lookup the specific transaction by ID. + * + * #### Example + * ```typescript + * const txId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .txid(txId) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupTransactionByID(txnId).do()` + * @param txid + * @category query + */ + txid(txid: string) { + this.query.txid = txid; + return this; + } + + /** + * Include results for the specified round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .round(targetBlock) + * .do(); + * ``` + * + * @param round + * @category query + */ + round(round: number) { + this.query.round = round; + return this; + } + + /** + * Include results at or after the specified min-round. + * + * #### Example + * ```typescript + * const minRound = 18309917; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .minRound(minRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + minRound(round: number) { + this.query['min-round'] = round; + return this; + } + + /** + * Include results at or before the specified max-round. + * + * #### Example + * ```typescript + * const maxRound = 18309917; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .maxRound(maxRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + maxRound(round: number) { + this.query['max-round'] = round; + return this; + } + + /** + * Asset ID to filter with. + * + * #### Example + * ```typescript + * const assetID = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .assetID(assetID) + * .do(); + * ``` + * + * @param id + * @category query + */ + assetID(id: number) { + this.query['asset-id'] = id; + return this; + } + + /** + * Maximum number of results to return. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Include results before the given time. + * + * #### Example + * ```typescript + * const beforeTime = "2022-02-02T20:20:22.02Z"; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .beforeTime(beforeTime) + * .do(); + * ``` + * + * @param before - rfc3339 string + * @category query + */ + beforeTime(before: string) { + this.query['before-time'] = before; + return this; + } + + /** + * Include results after the given time. + * + * #### Example + * ```typescript + * const afterTime = "2022-10-21T00:00:11.55Z"; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .afterTime(afterTime) + * .do(); + * ``` + * + * @param after - rfc3339 string + * @category query + */ + afterTime(after: string) { + this.query['after-time'] = after; + return this; + } + + /** + * Filtered results should have an amount greater than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const minBalance = 300000; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const minBalance = 300000; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .assetID(assetID) + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * @remarks + * If you are looking for transactions with the currency amount greater than 0, simply construct the query without `currencyGreaterThan` because it doesn't accept `-1`, and passing the `0` `currency-greater-than` value would exclude transactions with a 0 amount. + * + * @param greater + * @category query + */ + currencyGreaterThan(greater: number) { + this.query['currency-greater-than'] = greater; + return this; + } + + /** + * Filtered results should have an amount less than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const maxBalance = 500000; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const maxBalance = 500000; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .assetID(assetID) + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * @param lesser + * @category query + */ + currencyLessThan(lesser: number) { + this.query['currency-less-than'] = lesser; + return this; + } + + /** + * The next page of results. Use the next token provided by the previous results. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * + * const accountTxnsPage1 = await indexerClient + * .lookupAccountTransactions(address) + * .limit(maxResults) + * .do(); + * + * const accountTxnsPage2 = await indexerClient + * .lookupAccountTransactions(address) + * .limit(maxResults) + * .nextToken(accountTxnsPage1["next-token"]) + * .do(); + * ``` + * + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Whether or not to include rekeying transactions. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .rekeyTo(false) + * .do(); + * ``` + * + * @param rekeyTo + * @category query + */ + rekeyTo(rekeyTo: boolean) { + this.query['rekey-to'] = rekeyTo; + return this; + } +} diff --git a/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts b/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts new file mode 100644 index 0000000..94bc801 --- /dev/null +++ b/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts @@ -0,0 +1,51 @@ +import { Buffer } from 'buffer'; +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; +import { Box } from './models/types'; + +export default class LookupApplicationBoxByIDandName extends JSONRequest< + Box, + Record +> { + /** + * Returns information about indexed application boxes. + * + * #### Example + * ```typescript + * const boxName = Buffer.from("foo"); + * const boxResponse = await indexerClient + * .LookupApplicationBoxByIDandName(1234, boxName) + * .do(); + * const boxValue = boxResponse.value; + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idbox) + * @oaram index - application index. + * @category GET + */ + constructor( + c: HTTPClient, + intDecoding: IntDecoding, + private index: number, + boxName: Uint8Array + ) { + super(c, intDecoding); + this.index = index; + // Encode query in base64 format and append the encoding prefix. + const encodedName = Buffer.from(boxName).toString('base64'); + this.query.name = encodeURI(`b64:${encodedName}`); + } + + /** + * @returns `/v2/applications/${index}/box` + */ + path() { + return `/v2/applications/${this.index}/box`; + } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): Box { + return Box.from_obj_for_encoding(body); + } +} diff --git a/src/client/v2/indexer/lookupApplicationLogs.ts b/src/client/v2/indexer/lookupApplicationLogs.ts new file mode 100644 index 0000000..2b945ba --- /dev/null +++ b/src/client/v2/indexer/lookupApplicationLogs.ts @@ -0,0 +1,156 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LookupApplicationLogs extends JSONRequest { + /** + * Returns log messages generated by the passed in application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const appLogs = await indexerClient.lookupApplicationLogs(appId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idlogs) + * @param appID - The ID of the application which generated the logs. + * @category GET + */ + constructor(c: HTTPClient, intDecoding: IntDecoding, private appID: number) { + super(c, intDecoding); + this.appID = appID; + } + + /** + * @returns `/v2/applications/${appID}/logs` + */ + path() { + return `/v2/applications/${this.appID}/logs`; + } + + /** + * Limit results for pagination. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const appLogs = await indexerClient + * .lookupApplicationLogs(appId) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Include results at or after the specified min-round. + * + * #### Example + * ```typescript + * const minRound = 18309917; + * const appLogs = await indexerClient + * .lookupApplicationLogs(appId) + * .minRound(minRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + minRound(round: number) { + this.query['min-round'] = round; + return this; + } + + /** + * Include results at or before the specified max-round. + * + * #### Example + * ```typescript + * const maxRound = 18309917; + * const appLogs = await indexerClient + * .lookupApplicationLogs(appId) + * .maxRound(maxRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + maxRound(round: number) { + this.query['max-round'] = round; + return this; + } + + /** + * The next page of results. + * + * #### Example + * ```typescript + * const maxResults = 25; + * + * const appLogsPage1 = await indexerClient + * .lookupApplicationLogs(appId) + * .limit(maxResults) + * .do(); + * + * const appLogsPage2 = await indexerClient + * .lookupApplicationLogs(appId) + * .limit(maxResults) + * .nextToken(appLogsPage1["next-token"]) + * .do(); + * ``` + * + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Only include transactions with this sender address. + * + * #### Example + * ```typescript + * const sender = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const appLogs = await indexerClient + * .lookupApplicationLogs(appId) + * .sender(sender) + * .do(); + * ``` + * + * @param senderAddress + * @category query + */ + sender(senderAddress: string) { + this.query['sender-address'] = senderAddress; + return this; + } + + /** + * Lookup the specific transaction by ID. + * + * #### Example + * ```typescript + * const txId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const appLogs = await indexerClient + * .lookupApplicationLogs(appId) + * .txid(txId) + * .do(); + * ``` + * + * @param txid + * @category query + */ + txid(txid: string) { + this.query.txid = txid; + return this; + } +} diff --git a/src/client/v2/indexer/lookupApplications.ts b/src/client/v2/indexer/lookupApplications.ts new file mode 100644 index 0000000..8fb4429 --- /dev/null +++ b/src/client/v2/indexer/lookupApplications.ts @@ -0,0 +1,59 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LookupApplications extends JSONRequest { + /** + * Returns information about the passed application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const appInfo = await indexerClient.lookupApplications(appId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-id) + * @param index - The ID of the application to look up. + * @category GET + */ + constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { + super(c, intDecoding); + this.index = index; + } + + /** + * @returns `/v2/applications/${index}` + */ + path() { + return `/v2/applications/${this.index}`; + } + + /** + * Includes all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example 1 + * ```typescript + * const appId = 60553466; + * const appInfo = await indexerClient + * .lookupApplications(appId) + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const appId = 60553466; + * const appInfo = await indexerClient + * .lookupApplications(appId) + * .includeAll() + * .do(); + * ``` + * + * @param value - default true when called without passing a value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } +} diff --git a/src/client/v2/indexer/lookupAssetBalances.ts b/src/client/v2/indexer/lookupAssetBalances.ts new file mode 100644 index 0000000..d11795b --- /dev/null +++ b/src/client/v2/indexer/lookupAssetBalances.ts @@ -0,0 +1,146 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LookupAssetBalances extends JSONRequest { + /** + * Returns the list of accounts which hold the given asset and their balance. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetBalances = await indexerClient.lookupAssetBalances(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-idbalances) + * @param index - The asset ID to look up. + */ + constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { + super(c, intDecoding); + this.index = index; + } + + /** + * @returns `/v2/assets/${index}/balances` + */ + path() { + return `/v2/assets/${this.index}/balances`; + } + + /** + * Limit results for pagination. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const maxResults = 20; + * const assetBalances = await indexerClient + * .lookupAssetBalances(assetId) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Filtered results should have an asset balance greater than this value. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const minBalance = 1000000; + * const assetBalances = await indexerClient + * .lookupAssetBalances(assetId) + * .currencyGreaterThan(minBalance) + * .do(); + * ``` + * @param greater + * @category query + */ + currencyGreaterThan(greater: number) { + this.query['currency-greater-than'] = greater; + return this; + } + + /** + * Filtered results should have an asset balance less than this value. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const maxBalance = 2000000; + * const assetBalances = await indexerClient + * .lookupAssetBalances(assetId) + * .currencyLessThan(maxBalance) + * .do(); + * ``` + * @param lesser + * @category query + */ + currencyLessThan(lesser: number) { + this.query['currency-less-than'] = lesser; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const maxResults = 20; + * + * const assetBalancesPage1 = await indexerClient + * .lookupAssetBalances(assetId) + * .limit(maxResults) + * .do(); + * + * const assetBalancesPage2 = await indexerClient + * .lookupAssetBalances(assetId) + * .limit(maxResults) + * .nextToken(assetBalancesPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates. + * + * #### Example 1 + * ```typescript + * const assetId = 163650; + * const assetBalances = await indexerClient + * .lookupAssetBalances(assetId) + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetId = 163650; + * const assetBalances = await indexerClient + * .lookupAssetBalances(assetId) + * .includeAll() + * .do(); + * ``` + * + * @param value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } +} diff --git a/src/client/v2/indexer/lookupAssetByID.ts b/src/client/v2/indexer/lookupAssetByID.ts new file mode 100644 index 0000000..76331fb --- /dev/null +++ b/src/client/v2/indexer/lookupAssetByID.ts @@ -0,0 +1,58 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LookupAssetByID extends JSONRequest { + /** + * Returns asset information of the queried asset. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetInfo = await indexerClient.lookupAssetByID(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-id) + * @param index - The asset ID to look up. + */ + constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { + super(c, intDecoding); + this.index = index; + } + + /** + * @returns `/v2/assets/${index}` + */ + path() { + return `/v2/assets/${this.index}`; + } + + /** + * Includes all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example 1 + * ```typescript + * const assetId = 163650; + * const assetInfo = await indexerClient + * .lookupAssetByID(assetId) + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetId = 163650; + * const assetInfo = await indexerClient + * .lookupAssetByID(assetId) + * .includeAll() + * .do(); + * ``` + * + * @param value - default true when called without passing a value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } +} diff --git a/src/client/v2/indexer/lookupAssetTransactions.ts b/src/client/v2/indexer/lookupAssetTransactions.ts new file mode 100644 index 0000000..27b95b6 --- /dev/null +++ b/src/client/v2/indexer/lookupAssetTransactions.ts @@ -0,0 +1,398 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; +import { base64StringFunnel } from './lookupAccountTransactions'; + +export default class LookupAssetTransactions extends JSONRequest { + /** + * Returns transactions relating to the given asset. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient.lookupAssetTransactions(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-idtransactions) + * @param index - The asset ID to look up. + */ + constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { + super(c, intDecoding); + this.index = index; + } + + /** + * @returns `/v2/assets/${index}/transactions` + */ + path() { + return `/v2/assets/${this.index}/transactions`; + } + + /** + * Specifies a prefix which must be contained in the note field. + * + * #### Example + * ```typescript + * const notePrefixBase64Encoded = "Y3JlYXRl"; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .notePrefix(notePrefixBase64Encoded) + * .do(); + * ``` + * + * @param prefix - base64 string or uint8array + * @category query + */ + notePrefix(prefix: Uint8Array | string) { + this.query['note-prefix'] = base64StringFunnel(prefix); + return this; + } + + /** + * Type of transaction to filter with. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .txType("axfer") + * .do(); + * ``` + * + * @param type - one of `pay`, `keyreg`, `acfg`, `axfer`, `afrz`, `appl` + * @category query + */ + txType(type: string) { + this.query['tx-type'] = type; + return this; + } + + /** + * Type of signature to filter with. + * - sig: Standard + * - msig: MultiSig + * - lsig: LogicSig + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .sigType("lsig") + * .do(); + * ``` + * + * @param type - one of `sig`, `msig`, `lsig` + * @category query + */ + sigType(type: string) { + this.query['sig-type'] = type; + return this; + } + + /** + * Lookup the specific transaction by ID. + * + * #### Example + * ```typescript + * const txId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .txid(txId) + * .do(); + * ``` + * + * @param txid + * @category query + */ + txid(txid: string) { + this.query.txid = txid; + return this; + } + + /** + * Include results for the specified round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .round(targetBlock) + * .do(); + * ``` + * + * @param round + * @category query + */ + round(round: number) { + this.query.round = round; + return this; + } + + /** + * Include results at or after the specified min-round. + * + * #### Example + * ```typescript + * const minRound = 18309917; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .minRound(minRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + minRound(round: number) { + this.query['min-round'] = round; + return this; + } + + /** + * Include results at or before the specified max-round. + * + * #### Example + * ```typescript + * const maxRound = 18309917; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .maxRound(maxRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + maxRound(round: number) { + this.query['max-round'] = round; + return this; + } + + /** + * Maximum number of results to return. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Include results before the given time. + * + * #### Example + * ```typescript + * const beforeTime = "2022-02-02T20:20:22.02Z"; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .beforeTime(beforeTime) + * .do(); + * ``` + * + * @param before - rfc3339 string + * @category query + */ + beforeTime(before: string) { + this.query['before-time'] = before; + return this; + } + + /** + * Include results after the given time. + * + * #### Example + * ```typescript + * const afterTime = "2022-10-21T00:00:11.55Z"; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .afterTime(afterTime) + * .do(); + * ``` + * + * @param after - rfc3339 string + * @category query + */ + afterTime(after: string) { + this.query['after-time'] = after; + return this; + } + + /** + * Filtered results should have an amount greater than this value, as int, representing asset units. + * + * #### Example + * ```typescript + * const minBalance = 300000; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * + * @remarks + * If you are looking for transactions with the currency amount greater than 0, simply construct the query without `currencyGreaterThan` because it doesn't accept `-1`, and passing the `0` `currency-greater-than` value would exclude transactions with a 0 amount. + * + * @param greater + * @category query + */ + currencyGreaterThan(greater: number) { + this.query['currency-greater-than'] = greater; + return this; + } + + /** + * Filtered results should have an amount less than this value, as int, representing asset units. + * + * #### Example + * ```typescript + * const maxBalance = 500000; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * @param lesser + * @category query + */ + currencyLessThan(lesser: number) { + this.query['currency-less-than'] = lesser; + return this; + } + + /** + * Combined with address, defines what address to filter on, as string. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const role = "sender"; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .address(address) + * .addressRole(role) + * .do(); + * ``` + * + * @param role - one of `sender`, `receiver`, `freeze-target` + * @category query + */ + addressRole(role: string) { + this.query['address-role'] = role; + return this; + } + + /** + * Only include transactions with this address in one of the transaction fields. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .address(address) + * .do(); + * ``` + * + * @param address + * @category query + */ + address(address: string) { + this.query.address = address; + return this; + } + + /** + * Whether or not to consider the `close-to` field as a receiver when filtering transactions, as bool. Set to `true` to ignore `close-to`. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .excludeCloseTo(true) + * .do(); + * ``` + * + * @param exclude + * @category query + */ + excludeCloseTo(exclude: boolean) { + this.query['exclude-close-to'] = exclude; + return this; + } + + /** + * The next page of results. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const assetId = 163650; + * + * const assetTxnsPage1 = await indexerClient + * .lookupAssetTransactions(assetId) + * .limit(maxResults) + * .do(); + * + * const assetTxnsPage2 = await indexerClient + * .lookupAssetTransactions(assetId) + * .limit(maxResults) + * .nextToken(assetTxnsPage1["next-token"]) + * .do(); + * ``` + * + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Whether or not to include rekeying transactions. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .rekeyTo(false) + * .do(); + * ``` + * + * @param rekeyTo + * @category query + */ + rekeyTo(rekeyTo: boolean) { + this.query['rekey-to'] = rekeyTo; + return this; + } +} diff --git a/src/client/v2/indexer/lookupBlock.ts b/src/client/v2/indexer/lookupBlock.ts new file mode 100644 index 0000000..3b50307 --- /dev/null +++ b/src/client/v2/indexer/lookupBlock.ts @@ -0,0 +1,39 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LookupBlock extends JSONRequest { + /** + * Returns the block for the passed round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const blockInfo = await indexerClient.lookupBlock(targetBlock).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2blocksround-number) + * @param round - The number of the round to look up. + * @category GET + */ + constructor(c: HTTPClient, intDecoding: IntDecoding, private round: number) { + super(c, intDecoding); + this.round = round; + } + + /** + * @returns `/v2/blocks/${round}` + */ + path() { + return `/v2/blocks/${this.round}`; + } + + /** + * Header only flag. When this is set to true, returned block does not contain the + * transactions. + */ + headerOnly(headerOnly: boolean) { + this.query['header-only'] = headerOnly; + return this; + } +} diff --git a/src/client/v2/indexer/lookupTransactionByID.ts b/src/client/v2/indexer/lookupTransactionByID.ts new file mode 100644 index 0000000..d834db3 --- /dev/null +++ b/src/client/v2/indexer/lookupTransactionByID.ts @@ -0,0 +1,30 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; + +export default class LookupTransactionByID extends JSONRequest { + /** + * Returns information about the given transaction. + * + * #### Example + * ```typescript + * const txnId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const txnInfo = await indexerClient.lookupTransactionByID(txnId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2transactionstxid) + * @param txID - The ID of the transaction to look up. + * @category GET + */ + constructor(c: HTTPClient, intDecoding: IntDecoding, private txID: string) { + super(c, intDecoding); + this.txID = txID; + } + + /** + * @returns `/v2/transactions/${txID}` + */ + path() { + return `/v2/transactions/${this.txID}`; + } +} diff --git a/src/client/v2/indexer/makeHealthCheck.ts b/src/client/v2/indexer/makeHealthCheck.ts new file mode 100644 index 0000000..61059b0 --- /dev/null +++ b/src/client/v2/indexer/makeHealthCheck.ts @@ -0,0 +1,23 @@ +import JSONRequest from '../jsonrequest'; + +/** + * Returns the health object for the service. + * Returns 200 if healthy. + * + * #### Example + * ```typescript + * const health = await indexerClient.makeHealthCheck().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-health) + * @category GET + */ +export default class MakeHealthCheck extends JSONRequest { + /** + * @returns `/health` + */ + // eslint-disable-next-line class-methods-use-this + path() { + return '/health'; + } +} diff --git a/src/client/v2/indexer/models/types.ts b/src/client/v2/indexer/models/types.ts new file mode 100644 index 0000000..12b0555 --- /dev/null +++ b/src/client/v2/indexer/models/types.ts @@ -0,0 +1,5668 @@ +/** + * NOTICE: This file was generated. Editing this file manually is not recommended. + */ + +/* eslint-disable no-use-before-define */ +import { Buffer } from 'buffer'; +import BaseModel from '../../basemodel'; + +/** + * Account information at a given round. + * Definition: + * data/basics/userBalance.go : AccountData + */ +export class Account extends BaseModel { + /** + * the account public key + */ + public address: string; + + /** + * (algo) total number of MicroAlgos in the account + */ + public amount: number | bigint; + + /** + * specifies the amount of MicroAlgos in the account, without the pending rewards. + */ + public amountWithoutPendingRewards: number | bigint; + + /** + * amount of MicroAlgos of pending rewards in this account. + */ + public pendingRewards: number | bigint; + + /** + * (ern) total rewards of MicroAlgos the account has received, including pending + * rewards. + */ + public rewards: number | bigint; + + /** + * The round for which this information is relevant. + */ + public round: number | bigint; + + /** + * (onl) delegation status of the account's MicroAlgos + * * Offline - indicates that the associated account is delegated. + * * Online - indicates that the associated account used as part of the delegation + * pool. + * * NotParticipating - indicates that the associated account is neither a + * delegator nor a delegate. + */ + public status: string; + + /** + * The count of all applications that have been opted in, equivalent to the count + * of application local data (AppLocalState objects) stored in this account. + */ + public totalAppsOptedIn: number | bigint; + + /** + * The count of all assets that have been opted in, equivalent to the count of + * AssetHolding objects held by this account. + */ + public totalAssetsOptedIn: number | bigint; + + /** + * For app-accounts only. The total number of bytes allocated for the keys and + * values of boxes which belong to the associated application. + */ + public totalBoxBytes: number | bigint; + + /** + * For app-accounts only. The total number of boxes which belong to the associated + * application. + */ + public totalBoxes: number | bigint; + + /** + * The count of all apps (AppParams objects) created by this account. + */ + public totalCreatedApps: number | bigint; + + /** + * The count of all assets (AssetParams objects) created by this account. + */ + public totalCreatedAssets: number | bigint; + + /** + * (appl) applications local data stored in this account. + * Note the raw object uses `map[int] -> AppLocalState` for this type. + */ + public appsLocalState?: ApplicationLocalState[]; + + /** + * (teap) the sum of all extra application program pages for this account. + */ + public appsTotalExtraPages?: number | bigint; + + /** + * (tsch) stores the sum of all of the local schemas and global schemas in this + * account. + * Note: the raw account uses `StateSchema` for this type. + */ + public appsTotalSchema?: ApplicationStateSchema; + + /** + * (asset) assets held by this account. + * Note the raw object uses `map[int] -> AssetHolding` for this type. + */ + public assets?: AssetHolding[]; + + /** + * (spend) the address against which signing should be checked. If empty, the + * address of the current account is used. This field can be updated in any + * transaction by setting the RekeyTo field. + */ + public authAddr?: string; + + /** + * Round during which this account was most recently closed. + */ + public closedAtRound?: number | bigint; + + /** + * (appp) parameters of applications created by this account including app global + * data. + * Note: the raw account uses `map[int] -> AppParams` for this type. + */ + public createdApps?: Application[]; + + /** + * (apar) parameters of assets created by this account. + * Note: the raw account uses `map[int] -> Asset` for this type. + */ + public createdAssets?: Asset[]; + + /** + * Round during which this account first appeared in a transaction. + */ + public createdAtRound?: number | bigint; + + /** + * Whether or not this account is currently closed. + */ + public deleted?: boolean; + + /** + * AccountParticipation describes the parameters used by this account in consensus + * protocol. + */ + public participation?: AccountParticipation; + + /** + * (ebase) used as part of the rewards computation. Only applicable to accounts + * which are participating. + */ + public rewardBase?: number | bigint; + + /** + * Indicates what type of signature is used by this account, must be one of: + * * sig + * * msig + * * lsig + * * or null if unknown + */ + public sigType?: string; + + /** + * Creates a new `Account` object. + * @param address - the account public key + * @param amount - (algo) total number of MicroAlgos in the account + * @param amountWithoutPendingRewards - specifies the amount of MicroAlgos in the account, without the pending rewards. + * @param pendingRewards - amount of MicroAlgos of pending rewards in this account. + * @param rewards - (ern) total rewards of MicroAlgos the account has received, including pending + * rewards. + * @param round - The round for which this information is relevant. + * @param status - (onl) delegation status of the account's MicroAlgos + * * Offline - indicates that the associated account is delegated. + * * Online - indicates that the associated account used as part of the delegation + * pool. + * * NotParticipating - indicates that the associated account is neither a + * delegator nor a delegate. + * @param totalAppsOptedIn - The count of all applications that have been opted in, equivalent to the count + * of application local data (AppLocalState objects) stored in this account. + * @param totalAssetsOptedIn - The count of all assets that have been opted in, equivalent to the count of + * AssetHolding objects held by this account. + * @param totalBoxBytes - For app-accounts only. The total number of bytes allocated for the keys and + * values of boxes which belong to the associated application. + * @param totalBoxes - For app-accounts only. The total number of boxes which belong to the associated + * application. + * @param totalCreatedApps - The count of all apps (AppParams objects) created by this account. + * @param totalCreatedAssets - The count of all assets (AssetParams objects) created by this account. + * @param appsLocalState - (appl) applications local data stored in this account. + * Note the raw object uses `map[int] -> AppLocalState` for this type. + * @param appsTotalExtraPages - (teap) the sum of all extra application program pages for this account. + * @param appsTotalSchema - (tsch) stores the sum of all of the local schemas and global schemas in this + * account. + * Note: the raw account uses `StateSchema` for this type. + * @param assets - (asset) assets held by this account. + * Note the raw object uses `map[int] -> AssetHolding` for this type. + * @param authAddr - (spend) the address against which signing should be checked. If empty, the + * address of the current account is used. This field can be updated in any + * transaction by setting the RekeyTo field. + * @param closedAtRound - Round during which this account was most recently closed. + * @param createdApps - (appp) parameters of applications created by this account including app global + * data. + * Note: the raw account uses `map[int] -> AppParams` for this type. + * @param createdAssets - (apar) parameters of assets created by this account. + * Note: the raw account uses `map[int] -> Asset` for this type. + * @param createdAtRound - Round during which this account first appeared in a transaction. + * @param deleted - Whether or not this account is currently closed. + * @param participation - AccountParticipation describes the parameters used by this account in consensus + * protocol. + * @param rewardBase - (ebase) used as part of the rewards computation. Only applicable to accounts + * which are participating. + * @param sigType - Indicates what type of signature is used by this account, must be one of: + * * sig + * * msig + * * lsig + * * or null if unknown + */ + constructor({ + address, + amount, + amountWithoutPendingRewards, + pendingRewards, + rewards, + round, + status, + totalAppsOptedIn, + totalAssetsOptedIn, + totalBoxBytes, + totalBoxes, + totalCreatedApps, + totalCreatedAssets, + appsLocalState, + appsTotalExtraPages, + appsTotalSchema, + assets, + authAddr, + closedAtRound, + createdApps, + createdAssets, + createdAtRound, + deleted, + participation, + rewardBase, + sigType, + }: { + address: string; + amount: number | bigint; + amountWithoutPendingRewards: number | bigint; + pendingRewards: number | bigint; + rewards: number | bigint; + round: number | bigint; + status: string; + totalAppsOptedIn: number | bigint; + totalAssetsOptedIn: number | bigint; + totalBoxBytes: number | bigint; + totalBoxes: number | bigint; + totalCreatedApps: number | bigint; + totalCreatedAssets: number | bigint; + appsLocalState?: ApplicationLocalState[]; + appsTotalExtraPages?: number | bigint; + appsTotalSchema?: ApplicationStateSchema; + assets?: AssetHolding[]; + authAddr?: string; + closedAtRound?: number | bigint; + createdApps?: Application[]; + createdAssets?: Asset[]; + createdAtRound?: number | bigint; + deleted?: boolean; + participation?: AccountParticipation; + rewardBase?: number | bigint; + sigType?: string; + }) { + super(); + this.address = address; + this.amount = amount; + this.amountWithoutPendingRewards = amountWithoutPendingRewards; + this.pendingRewards = pendingRewards; + this.rewards = rewards; + this.round = round; + this.status = status; + this.totalAppsOptedIn = totalAppsOptedIn; + this.totalAssetsOptedIn = totalAssetsOptedIn; + this.totalBoxBytes = totalBoxBytes; + this.totalBoxes = totalBoxes; + this.totalCreatedApps = totalCreatedApps; + this.totalCreatedAssets = totalCreatedAssets; + this.appsLocalState = appsLocalState; + this.appsTotalExtraPages = appsTotalExtraPages; + this.appsTotalSchema = appsTotalSchema; + this.assets = assets; + this.authAddr = authAddr; + this.closedAtRound = closedAtRound; + this.createdApps = createdApps; + this.createdAssets = createdAssets; + this.createdAtRound = createdAtRound; + this.deleted = deleted; + this.participation = participation; + this.rewardBase = rewardBase; + this.sigType = sigType; + + this.attribute_map = { + address: 'address', + amount: 'amount', + amountWithoutPendingRewards: 'amount-without-pending-rewards', + pendingRewards: 'pending-rewards', + rewards: 'rewards', + round: 'round', + status: 'status', + totalAppsOptedIn: 'total-apps-opted-in', + totalAssetsOptedIn: 'total-assets-opted-in', + totalBoxBytes: 'total-box-bytes', + totalBoxes: 'total-boxes', + totalCreatedApps: 'total-created-apps', + totalCreatedAssets: 'total-created-assets', + appsLocalState: 'apps-local-state', + appsTotalExtraPages: 'apps-total-extra-pages', + appsTotalSchema: 'apps-total-schema', + assets: 'assets', + authAddr: 'auth-addr', + closedAtRound: 'closed-at-round', + createdApps: 'created-apps', + createdAssets: 'created-assets', + createdAtRound: 'created-at-round', + deleted: 'deleted', + participation: 'participation', + rewardBase: 'reward-base', + sigType: 'sig-type', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): Account { + /* eslint-disable dot-notation */ + if (typeof data['address'] === 'undefined') + throw new Error(`Response is missing required field 'address': ${data}`); + if (typeof data['amount'] === 'undefined') + throw new Error(`Response is missing required field 'amount': ${data}`); + if (typeof data['amount-without-pending-rewards'] === 'undefined') + throw new Error( + `Response is missing required field 'amount-without-pending-rewards': ${data}` + ); + if (typeof data['pending-rewards'] === 'undefined') + throw new Error( + `Response is missing required field 'pending-rewards': ${data}` + ); + if (typeof data['rewards'] === 'undefined') + throw new Error(`Response is missing required field 'rewards': ${data}`); + if (typeof data['round'] === 'undefined') + throw new Error(`Response is missing required field 'round': ${data}`); + if (typeof data['status'] === 'undefined') + throw new Error(`Response is missing required field 'status': ${data}`); + if (typeof data['total-apps-opted-in'] === 'undefined') + throw new Error( + `Response is missing required field 'total-apps-opted-in': ${data}` + ); + if (typeof data['total-assets-opted-in'] === 'undefined') + throw new Error( + `Response is missing required field 'total-assets-opted-in': ${data}` + ); + if (typeof data['total-box-bytes'] === 'undefined') + throw new Error( + `Response is missing required field 'total-box-bytes': ${data}` + ); + if (typeof data['total-boxes'] === 'undefined') + throw new Error( + `Response is missing required field 'total-boxes': ${data}` + ); + if (typeof data['total-created-apps'] === 'undefined') + throw new Error( + `Response is missing required field 'total-created-apps': ${data}` + ); + if (typeof data['total-created-assets'] === 'undefined') + throw new Error( + `Response is missing required field 'total-created-assets': ${data}` + ); + return new Account({ + address: data['address'], + amount: data['amount'], + amountWithoutPendingRewards: data['amount-without-pending-rewards'], + pendingRewards: data['pending-rewards'], + rewards: data['rewards'], + round: data['round'], + status: data['status'], + totalAppsOptedIn: data['total-apps-opted-in'], + totalAssetsOptedIn: data['total-assets-opted-in'], + totalBoxBytes: data['total-box-bytes'], + totalBoxes: data['total-boxes'], + totalCreatedApps: data['total-created-apps'], + totalCreatedAssets: data['total-created-assets'], + appsLocalState: + typeof data['apps-local-state'] !== 'undefined' + ? data['apps-local-state'].map( + ApplicationLocalState.from_obj_for_encoding + ) + : undefined, + appsTotalExtraPages: data['apps-total-extra-pages'], + appsTotalSchema: + typeof data['apps-total-schema'] !== 'undefined' + ? ApplicationStateSchema.from_obj_for_encoding( + data['apps-total-schema'] + ) + : undefined, + assets: + typeof data['assets'] !== 'undefined' + ? data['assets'].map(AssetHolding.from_obj_for_encoding) + : undefined, + authAddr: data['auth-addr'], + closedAtRound: data['closed-at-round'], + createdApps: + typeof data['created-apps'] !== 'undefined' + ? data['created-apps'].map(Application.from_obj_for_encoding) + : undefined, + createdAssets: + typeof data['created-assets'] !== 'undefined' + ? data['created-assets'].map(Asset.from_obj_for_encoding) + : undefined, + createdAtRound: data['created-at-round'], + deleted: data['deleted'], + participation: + typeof data['participation'] !== 'undefined' + ? AccountParticipation.from_obj_for_encoding(data['participation']) + : undefined, + rewardBase: data['reward-base'], + sigType: data['sig-type'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * AccountParticipation describes the parameters used by this account in consensus + * protocol. + */ +export class AccountParticipation extends BaseModel { + /** + * (sel) Selection public key (if any) currently registered for this round. + */ + public selectionParticipationKey: Uint8Array; + + /** + * (voteFst) First round for which this participation is valid. + */ + public voteFirstValid: number | bigint; + + /** + * (voteKD) Number of subkeys in each batch of participation keys. + */ + public voteKeyDilution: number | bigint; + + /** + * (voteLst) Last round for which this participation is valid. + */ + public voteLastValid: number | bigint; + + /** + * (vote) root participation public key (if any) currently registered for this + * round. + */ + public voteParticipationKey: Uint8Array; + + /** + * (stprf) Root of the state proof key (if any) + */ + public stateProofKey?: Uint8Array; + + /** + * Creates a new `AccountParticipation` object. + * @param selectionParticipationKey - (sel) Selection public key (if any) currently registered for this round. + * @param voteFirstValid - (voteFst) First round for which this participation is valid. + * @param voteKeyDilution - (voteKD) Number of subkeys in each batch of participation keys. + * @param voteLastValid - (voteLst) Last round for which this participation is valid. + * @param voteParticipationKey - (vote) root participation public key (if any) currently registered for this + * round. + * @param stateProofKey - (stprf) Root of the state proof key (if any) + */ + constructor({ + selectionParticipationKey, + voteFirstValid, + voteKeyDilution, + voteLastValid, + voteParticipationKey, + stateProofKey, + }: { + selectionParticipationKey: string | Uint8Array; + voteFirstValid: number | bigint; + voteKeyDilution: number | bigint; + voteLastValid: number | bigint; + voteParticipationKey: string | Uint8Array; + stateProofKey?: string | Uint8Array; + }) { + super(); + this.selectionParticipationKey = + typeof selectionParticipationKey === 'string' + ? new Uint8Array(Buffer.from(selectionParticipationKey, 'base64')) + : selectionParticipationKey; + this.voteFirstValid = voteFirstValid; + this.voteKeyDilution = voteKeyDilution; + this.voteLastValid = voteLastValid; + this.voteParticipationKey = + typeof voteParticipationKey === 'string' + ? new Uint8Array(Buffer.from(voteParticipationKey, 'base64')) + : voteParticipationKey; + this.stateProofKey = + typeof stateProofKey === 'string' + ? new Uint8Array(Buffer.from(stateProofKey, 'base64')) + : stateProofKey; + + this.attribute_map = { + selectionParticipationKey: 'selection-participation-key', + voteFirstValid: 'vote-first-valid', + voteKeyDilution: 'vote-key-dilution', + voteLastValid: 'vote-last-valid', + voteParticipationKey: 'vote-participation-key', + stateProofKey: 'state-proof-key', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): AccountParticipation { + /* eslint-disable dot-notation */ + if (typeof data['selection-participation-key'] === 'undefined') + throw new Error( + `Response is missing required field 'selection-participation-key': ${data}` + ); + if (typeof data['vote-first-valid'] === 'undefined') + throw new Error( + `Response is missing required field 'vote-first-valid': ${data}` + ); + if (typeof data['vote-key-dilution'] === 'undefined') + throw new Error( + `Response is missing required field 'vote-key-dilution': ${data}` + ); + if (typeof data['vote-last-valid'] === 'undefined') + throw new Error( + `Response is missing required field 'vote-last-valid': ${data}` + ); + if (typeof data['vote-participation-key'] === 'undefined') + throw new Error( + `Response is missing required field 'vote-participation-key': ${data}` + ); + return new AccountParticipation({ + selectionParticipationKey: data['selection-participation-key'], + voteFirstValid: data['vote-first-valid'], + voteKeyDilution: data['vote-key-dilution'], + voteLastValid: data['vote-last-valid'], + voteParticipationKey: data['vote-participation-key'], + stateProofKey: data['state-proof-key'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class AccountResponse extends BaseModel { + /** + * Account information at a given round. + * Definition: + * data/basics/userBalance.go : AccountData + */ + public account: Account; + + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + /** + * Creates a new `AccountResponse` object. + * @param account - Account information at a given round. + * Definition: + * data/basics/userBalance.go : AccountData + * @param currentRound - Round at which the results were computed. + */ + constructor({ + account, + currentRound, + }: { + account: Account; + currentRound: number | bigint; + }) { + super(); + this.account = account; + this.currentRound = currentRound; + + this.attribute_map = { + account: 'account', + currentRound: 'current-round', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): AccountResponse { + /* eslint-disable dot-notation */ + if (typeof data['account'] === 'undefined') + throw new Error(`Response is missing required field 'account': ${data}`); + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + return new AccountResponse({ + account: Account.from_obj_for_encoding(data['account']), + currentRound: data['current-round'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Application state delta. + */ +export class AccountStateDelta extends BaseModel { + public address: string; + + /** + * Application state delta. + */ + public delta: EvalDeltaKeyValue[]; + + /** + * Creates a new `AccountStateDelta` object. + * @param address - + * @param delta - Application state delta. + */ + constructor({ + address, + delta, + }: { + address: string; + delta: EvalDeltaKeyValue[]; + }) { + super(); + this.address = address; + this.delta = delta; + + this.attribute_map = { + address: 'address', + delta: 'delta', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): AccountStateDelta { + /* eslint-disable dot-notation */ + if (typeof data['address'] === 'undefined') + throw new Error(`Response is missing required field 'address': ${data}`); + if (!Array.isArray(data['delta'])) + throw new Error( + `Response is missing required array field 'delta': ${data}` + ); + return new AccountStateDelta({ + address: data['address'], + delta: data['delta'].map(EvalDeltaKeyValue.from_obj_for_encoding), + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class AccountsResponse extends BaseModel { + public accounts: Account[]; + + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `AccountsResponse` object. + * @param accounts - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + accounts, + currentRound, + nextToken, + }: { + accounts: Account[]; + currentRound: number | bigint; + nextToken?: string; + }) { + super(); + this.accounts = accounts; + this.currentRound = currentRound; + this.nextToken = nextToken; + + this.attribute_map = { + accounts: 'accounts', + currentRound: 'current-round', + nextToken: 'next-token', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): AccountsResponse { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['accounts'])) + throw new Error( + `Response is missing required array field 'accounts': ${data}` + ); + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + return new AccountsResponse({ + accounts: data['accounts'].map(Account.from_obj_for_encoding), + currentRound: data['current-round'], + nextToken: data['next-token'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Application index and its parameters + */ +export class Application extends BaseModel { + /** + * (appidx) application index. + */ + public id: number | bigint; + + /** + * (appparams) application parameters. + */ + public params: ApplicationParams; + + /** + * Round when this application was created. + */ + public createdAtRound?: number | bigint; + + /** + * Whether or not this application is currently deleted. + */ + public deleted?: boolean; + + /** + * Round when this application was deleted. + */ + public deletedAtRound?: number | bigint; + + /** + * Creates a new `Application` object. + * @param id - (appidx) application index. + * @param params - (appparams) application parameters. + * @param createdAtRound - Round when this application was created. + * @param deleted - Whether or not this application is currently deleted. + * @param deletedAtRound - Round when this application was deleted. + */ + constructor({ + id, + params, + createdAtRound, + deleted, + deletedAtRound, + }: { + id: number | bigint; + params: ApplicationParams; + createdAtRound?: number | bigint; + deleted?: boolean; + deletedAtRound?: number | bigint; + }) { + super(); + this.id = id; + this.params = params; + this.createdAtRound = createdAtRound; + this.deleted = deleted; + this.deletedAtRound = deletedAtRound; + + this.attribute_map = { + id: 'id', + params: 'params', + createdAtRound: 'created-at-round', + deleted: 'deleted', + deletedAtRound: 'deleted-at-round', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): Application { + /* eslint-disable dot-notation */ + if (typeof data['id'] === 'undefined') + throw new Error(`Response is missing required field 'id': ${data}`); + if (typeof data['params'] === 'undefined') + throw new Error(`Response is missing required field 'params': ${data}`); + return new Application({ + id: data['id'], + params: ApplicationParams.from_obj_for_encoding(data['params']), + createdAtRound: data['created-at-round'], + deleted: data['deleted'], + deletedAtRound: data['deleted-at-round'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Stores local state associated with an application. + */ +export class ApplicationLocalState extends BaseModel { + /** + * The application which this local state is for. + */ + public id: number | bigint; + + /** + * (hsch) schema. + */ + public schema: ApplicationStateSchema; + + /** + * Round when account closed out of the application. + */ + public closedOutAtRound?: number | bigint; + + /** + * Whether or not the application local state is currently deleted from its + * account. + */ + public deleted?: boolean; + + /** + * (tkv) storage. + */ + public keyValue?: TealKeyValue[]; + + /** + * Round when the account opted into the application. + */ + public optedInAtRound?: number | bigint; + + /** + * Creates a new `ApplicationLocalState` object. + * @param id - The application which this local state is for. + * @param schema - (hsch) schema. + * @param closedOutAtRound - Round when account closed out of the application. + * @param deleted - Whether or not the application local state is currently deleted from its + * account. + * @param keyValue - (tkv) storage. + * @param optedInAtRound - Round when the account opted into the application. + */ + constructor({ + id, + schema, + closedOutAtRound, + deleted, + keyValue, + optedInAtRound, + }: { + id: number | bigint; + schema: ApplicationStateSchema; + closedOutAtRound?: number | bigint; + deleted?: boolean; + keyValue?: TealKeyValue[]; + optedInAtRound?: number | bigint; + }) { + super(); + this.id = id; + this.schema = schema; + this.closedOutAtRound = closedOutAtRound; + this.deleted = deleted; + this.keyValue = keyValue; + this.optedInAtRound = optedInAtRound; + + this.attribute_map = { + id: 'id', + schema: 'schema', + closedOutAtRound: 'closed-out-at-round', + deleted: 'deleted', + keyValue: 'key-value', + optedInAtRound: 'opted-in-at-round', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): ApplicationLocalState { + /* eslint-disable dot-notation */ + if (typeof data['id'] === 'undefined') + throw new Error(`Response is missing required field 'id': ${data}`); + if (typeof data['schema'] === 'undefined') + throw new Error(`Response is missing required field 'schema': ${data}`); + return new ApplicationLocalState({ + id: data['id'], + schema: ApplicationStateSchema.from_obj_for_encoding(data['schema']), + closedOutAtRound: data['closed-out-at-round'], + deleted: data['deleted'], + keyValue: + typeof data['key-value'] !== 'undefined' + ? data['key-value'].map(TealKeyValue.from_obj_for_encoding) + : undefined, + optedInAtRound: data['opted-in-at-round'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class ApplicationLocalStatesResponse extends BaseModel { + public appsLocalStates: ApplicationLocalState[]; + + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `ApplicationLocalStatesResponse` object. + * @param appsLocalStates - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + appsLocalStates, + currentRound, + nextToken, + }: { + appsLocalStates: ApplicationLocalState[]; + currentRound: number | bigint; + nextToken?: string; + }) { + super(); + this.appsLocalStates = appsLocalStates; + this.currentRound = currentRound; + this.nextToken = nextToken; + + this.attribute_map = { + appsLocalStates: 'apps-local-states', + currentRound: 'current-round', + nextToken: 'next-token', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): ApplicationLocalStatesResponse { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['apps-local-states'])) + throw new Error( + `Response is missing required array field 'apps-local-states': ${data}` + ); + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + return new ApplicationLocalStatesResponse({ + appsLocalStates: data['apps-local-states'].map( + ApplicationLocalState.from_obj_for_encoding + ), + currentRound: data['current-round'], + nextToken: data['next-token'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Stores the global information associated with an application. + */ +export class ApplicationLogData extends BaseModel { + /** + * (lg) Logs for the application being executed by the transaction. + */ + public logs: Uint8Array[]; + + /** + * Transaction ID + */ + public txid: string; + + /** + * Creates a new `ApplicationLogData` object. + * @param logs - (lg) Logs for the application being executed by the transaction. + * @param txid - Transaction ID + */ + constructor({ logs, txid }: { logs: Uint8Array[]; txid: string }) { + super(); + this.logs = logs; + this.txid = txid; + + this.attribute_map = { + logs: 'logs', + txid: 'txid', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): ApplicationLogData { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['logs'])) + throw new Error( + `Response is missing required array field 'logs': ${data}` + ); + if (typeof data['txid'] === 'undefined') + throw new Error(`Response is missing required field 'txid': ${data}`); + return new ApplicationLogData({ + logs: data['logs'], + txid: data['txid'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class ApplicationLogsResponse extends BaseModel { + /** + * (appidx) application index. + */ + public applicationId: number | bigint; + + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + public logData?: ApplicationLogData[]; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `ApplicationLogsResponse` object. + * @param applicationId - (appidx) application index. + * @param currentRound - Round at which the results were computed. + * @param logData - + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + applicationId, + currentRound, + logData, + nextToken, + }: { + applicationId: number | bigint; + currentRound: number | bigint; + logData?: ApplicationLogData[]; + nextToken?: string; + }) { + super(); + this.applicationId = applicationId; + this.currentRound = currentRound; + this.logData = logData; + this.nextToken = nextToken; + + this.attribute_map = { + applicationId: 'application-id', + currentRound: 'current-round', + logData: 'log-data', + nextToken: 'next-token', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): ApplicationLogsResponse { + /* eslint-disable dot-notation */ + if (typeof data['application-id'] === 'undefined') + throw new Error( + `Response is missing required field 'application-id': ${data}` + ); + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + return new ApplicationLogsResponse({ + applicationId: data['application-id'], + currentRound: data['current-round'], + logData: + typeof data['log-data'] !== 'undefined' + ? data['log-data'].map(ApplicationLogData.from_obj_for_encoding) + : undefined, + nextToken: data['next-token'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Stores the global information associated with an application. + */ +export class ApplicationParams extends BaseModel { + /** + * (approv) approval program. + */ + public approvalProgram: Uint8Array; + + /** + * (clearp) approval program. + */ + public clearStateProgram: Uint8Array; + + /** + * The address that created this application. This is the address where the + * parameters and global state for this application can be found. + */ + public creator?: string; + + /** + * (epp) the amount of extra program pages available to this app. + */ + public extraProgramPages?: number | bigint; + + /** + * [\gs) global schema + */ + public globalState?: TealKeyValue[]; + + /** + * [\gsch) global schema + */ + public globalStateSchema?: ApplicationStateSchema; + + /** + * [\lsch) local schema + */ + public localStateSchema?: ApplicationStateSchema; + + /** + * Creates a new `ApplicationParams` object. + * @param approvalProgram - (approv) approval program. + * @param clearStateProgram - (clearp) approval program. + * @param creator - The address that created this application. This is the address where the + * parameters and global state for this application can be found. + * @param extraProgramPages - (epp) the amount of extra program pages available to this app. + * @param globalState - [\gs) global schema + * @param globalStateSchema - [\gsch) global schema + * @param localStateSchema - [\lsch) local schema + */ + constructor({ + approvalProgram, + clearStateProgram, + creator, + extraProgramPages, + globalState, + globalStateSchema, + localStateSchema, + }: { + approvalProgram: string | Uint8Array; + clearStateProgram: string | Uint8Array; + creator?: string; + extraProgramPages?: number | bigint; + globalState?: TealKeyValue[]; + globalStateSchema?: ApplicationStateSchema; + localStateSchema?: ApplicationStateSchema; + }) { + super(); + this.approvalProgram = + typeof approvalProgram === 'string' + ? new Uint8Array(Buffer.from(approvalProgram, 'base64')) + : approvalProgram; + this.clearStateProgram = + typeof clearStateProgram === 'string' + ? new Uint8Array(Buffer.from(clearStateProgram, 'base64')) + : clearStateProgram; + this.creator = creator; + this.extraProgramPages = extraProgramPages; + this.globalState = globalState; + this.globalStateSchema = globalStateSchema; + this.localStateSchema = localStateSchema; + + this.attribute_map = { + approvalProgram: 'approval-program', + clearStateProgram: 'clear-state-program', + creator: 'creator', + extraProgramPages: 'extra-program-pages', + globalState: 'global-state', + globalStateSchema: 'global-state-schema', + localStateSchema: 'local-state-schema', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): ApplicationParams { + /* eslint-disable dot-notation */ + if (typeof data['approval-program'] === 'undefined') + throw new Error( + `Response is missing required field 'approval-program': ${data}` + ); + if (typeof data['clear-state-program'] === 'undefined') + throw new Error( + `Response is missing required field 'clear-state-program': ${data}` + ); + return new ApplicationParams({ + approvalProgram: data['approval-program'], + clearStateProgram: data['clear-state-program'], + creator: data['creator'], + extraProgramPages: data['extra-program-pages'], + globalState: + typeof data['global-state'] !== 'undefined' + ? data['global-state'].map(TealKeyValue.from_obj_for_encoding) + : undefined, + globalStateSchema: + typeof data['global-state-schema'] !== 'undefined' + ? ApplicationStateSchema.from_obj_for_encoding( + data['global-state-schema'] + ) + : undefined, + localStateSchema: + typeof data['local-state-schema'] !== 'undefined' + ? ApplicationStateSchema.from_obj_for_encoding( + data['local-state-schema'] + ) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class ApplicationResponse extends BaseModel { + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + /** + * Application index and its parameters + */ + public application?: Application; + + /** + * Creates a new `ApplicationResponse` object. + * @param currentRound - Round at which the results were computed. + * @param application - Application index and its parameters + */ + constructor({ + currentRound, + application, + }: { + currentRound: number | bigint; + application?: Application; + }) { + super(); + this.currentRound = currentRound; + this.application = application; + + this.attribute_map = { + currentRound: 'current-round', + application: 'application', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): ApplicationResponse { + /* eslint-disable dot-notation */ + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + return new ApplicationResponse({ + currentRound: data['current-round'], + application: + typeof data['application'] !== 'undefined' + ? Application.from_obj_for_encoding(data['application']) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Specifies maximums on the number of each type that may be stored. + */ +export class ApplicationStateSchema extends BaseModel { + /** + * (nbs) num of byte slices. + */ + public numByteSlice: number | bigint; + + /** + * (nui) num of uints. + */ + public numUint: number | bigint; + + /** + * Creates a new `ApplicationStateSchema` object. + * @param numByteSlice - (nbs) num of byte slices. + * @param numUint - (nui) num of uints. + */ + constructor({ + numByteSlice, + numUint, + }: { + numByteSlice: number | bigint; + numUint: number | bigint; + }) { + super(); + this.numByteSlice = numByteSlice; + this.numUint = numUint; + + this.attribute_map = { + numByteSlice: 'num-byte-slice', + numUint: 'num-uint', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): ApplicationStateSchema { + /* eslint-disable dot-notation */ + if (typeof data['num-byte-slice'] === 'undefined') + throw new Error( + `Response is missing required field 'num-byte-slice': ${data}` + ); + if (typeof data['num-uint'] === 'undefined') + throw new Error(`Response is missing required field 'num-uint': ${data}`); + return new ApplicationStateSchema({ + numByteSlice: data['num-byte-slice'], + numUint: data['num-uint'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class ApplicationsResponse extends BaseModel { + public applications: Application[]; + + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `ApplicationsResponse` object. + * @param applications - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + applications, + currentRound, + nextToken, + }: { + applications: Application[]; + currentRound: number | bigint; + nextToken?: string; + }) { + super(); + this.applications = applications; + this.currentRound = currentRound; + this.nextToken = nextToken; + + this.attribute_map = { + applications: 'applications', + currentRound: 'current-round', + nextToken: 'next-token', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): ApplicationsResponse { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['applications'])) + throw new Error( + `Response is missing required array field 'applications': ${data}` + ); + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + return new ApplicationsResponse({ + applications: data['applications'].map(Application.from_obj_for_encoding), + currentRound: data['current-round'], + nextToken: data['next-token'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Specifies both the unique identifier and the parameters for an asset + */ +export class Asset extends BaseModel { + /** + * unique asset identifier + */ + public index: number | bigint; + + /** + * AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + */ + public params: AssetParams; + + /** + * Round during which this asset was created. + */ + public createdAtRound?: number | bigint; + + /** + * Whether or not this asset is currently deleted. + */ + public deleted?: boolean; + + /** + * Round during which this asset was destroyed. + */ + public destroyedAtRound?: number | bigint; + + /** + * Creates a new `Asset` object. + * @param index - unique asset identifier + * @param params - AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + * @param createdAtRound - Round during which this asset was created. + * @param deleted - Whether or not this asset is currently deleted. + * @param destroyedAtRound - Round during which this asset was destroyed. + */ + constructor({ + index, + params, + createdAtRound, + deleted, + destroyedAtRound, + }: { + index: number | bigint; + params: AssetParams; + createdAtRound?: number | bigint; + deleted?: boolean; + destroyedAtRound?: number | bigint; + }) { + super(); + this.index = index; + this.params = params; + this.createdAtRound = createdAtRound; + this.deleted = deleted; + this.destroyedAtRound = destroyedAtRound; + + this.attribute_map = { + index: 'index', + params: 'params', + createdAtRound: 'created-at-round', + deleted: 'deleted', + destroyedAtRound: 'destroyed-at-round', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): Asset { + /* eslint-disable dot-notation */ + if (typeof data['index'] === 'undefined') + throw new Error(`Response is missing required field 'index': ${data}`); + if (typeof data['params'] === 'undefined') + throw new Error(`Response is missing required field 'params': ${data}`); + return new Asset({ + index: data['index'], + params: AssetParams.from_obj_for_encoding(data['params']), + createdAtRound: data['created-at-round'], + deleted: data['deleted'], + destroyedAtRound: data['destroyed-at-round'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class AssetBalancesResponse extends BaseModel { + public balances: MiniAssetHolding[]; + + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `AssetBalancesResponse` object. + * @param balances - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + balances, + currentRound, + nextToken, + }: { + balances: MiniAssetHolding[]; + currentRound: number | bigint; + nextToken?: string; + }) { + super(); + this.balances = balances; + this.currentRound = currentRound; + this.nextToken = nextToken; + + this.attribute_map = { + balances: 'balances', + currentRound: 'current-round', + nextToken: 'next-token', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): AssetBalancesResponse { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['balances'])) + throw new Error( + `Response is missing required array field 'balances': ${data}` + ); + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + return new AssetBalancesResponse({ + balances: data['balances'].map(MiniAssetHolding.from_obj_for_encoding), + currentRound: data['current-round'], + nextToken: data['next-token'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Describes an asset held by an account. + * Definition: + * data/basics/userBalance.go : AssetHolding + */ +export class AssetHolding extends BaseModel { + /** + * (a) number of units held. + */ + public amount: number | bigint; + + /** + * Asset ID of the holding. + */ + public assetId: number | bigint; + + /** + * (f) whether or not the holding is frozen. + */ + public isFrozen: boolean; + + /** + * Whether or not the asset holding is currently deleted from its account. + */ + public deleted?: boolean; + + /** + * Round during which the account opted into this asset holding. + */ + public optedInAtRound?: number | bigint; + + /** + * Round during which the account opted out of this asset holding. + */ + public optedOutAtRound?: number | bigint; + + /** + * Creates a new `AssetHolding` object. + * @param amount - (a) number of units held. + * @param assetId - Asset ID of the holding. + * @param isFrozen - (f) whether or not the holding is frozen. + * @param deleted - Whether or not the asset holding is currently deleted from its account. + * @param optedInAtRound - Round during which the account opted into this asset holding. + * @param optedOutAtRound - Round during which the account opted out of this asset holding. + */ + constructor({ + amount, + assetId, + isFrozen, + deleted, + optedInAtRound, + optedOutAtRound, + }: { + amount: number | bigint; + assetId: number | bigint; + isFrozen: boolean; + deleted?: boolean; + optedInAtRound?: number | bigint; + optedOutAtRound?: number | bigint; + }) { + super(); + this.amount = amount; + this.assetId = assetId; + this.isFrozen = isFrozen; + this.deleted = deleted; + this.optedInAtRound = optedInAtRound; + this.optedOutAtRound = optedOutAtRound; + + this.attribute_map = { + amount: 'amount', + assetId: 'asset-id', + isFrozen: 'is-frozen', + deleted: 'deleted', + optedInAtRound: 'opted-in-at-round', + optedOutAtRound: 'opted-out-at-round', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): AssetHolding { + /* eslint-disable dot-notation */ + if (typeof data['amount'] === 'undefined') + throw new Error(`Response is missing required field 'amount': ${data}`); + if (typeof data['asset-id'] === 'undefined') + throw new Error(`Response is missing required field 'asset-id': ${data}`); + if (typeof data['is-frozen'] === 'undefined') + throw new Error( + `Response is missing required field 'is-frozen': ${data}` + ); + return new AssetHolding({ + amount: data['amount'], + assetId: data['asset-id'], + isFrozen: data['is-frozen'], + deleted: data['deleted'], + optedInAtRound: data['opted-in-at-round'], + optedOutAtRound: data['opted-out-at-round'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class AssetHoldingsResponse extends BaseModel { + public assets: AssetHolding[]; + + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `AssetHoldingsResponse` object. + * @param assets - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + assets, + currentRound, + nextToken, + }: { + assets: AssetHolding[]; + currentRound: number | bigint; + nextToken?: string; + }) { + super(); + this.assets = assets; + this.currentRound = currentRound; + this.nextToken = nextToken; + + this.attribute_map = { + assets: 'assets', + currentRound: 'current-round', + nextToken: 'next-token', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): AssetHoldingsResponse { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['assets'])) + throw new Error( + `Response is missing required array field 'assets': ${data}` + ); + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + return new AssetHoldingsResponse({ + assets: data['assets'].map(AssetHolding.from_obj_for_encoding), + currentRound: data['current-round'], + nextToken: data['next-token'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + */ +export class AssetParams extends BaseModel { + /** + * The address that created this asset. This is the address where the parameters + * for this asset can be found, and also the address where unwanted asset units can + * be sent in the worst case. + */ + public creator: string; + + /** + * (dc) The number of digits to use after the decimal point when displaying this + * asset. If 0, the asset is not divisible. If 1, the base unit of the asset is in + * tenths. If 2, the base unit of the asset is in hundredths, and so on. This value + * must be between 0 and 19 (inclusive). + */ + public decimals: number | bigint; + + /** + * (t) The total number of units of this asset. + */ + public total: number | bigint; + + /** + * (c) Address of account used to clawback holdings of this asset. If empty, + * clawback is not permitted. + */ + public clawback?: string; + + /** + * (df) Whether holdings of this asset are frozen by default. + */ + public defaultFrozen?: boolean; + + /** + * (f) Address of account used to freeze holdings of this asset. If empty, freezing + * is not permitted. + */ + public freeze?: string; + + /** + * (m) Address of account used to manage the keys of this asset and to destroy it. + */ + public manager?: string; + + /** + * (am) A commitment to some unspecified asset metadata. The format of this + * metadata is up to the application. + */ + public metadataHash?: Uint8Array; + + /** + * (an) Name of this asset, as supplied by the creator. Included only when the + * asset name is composed of printable utf-8 characters. + */ + public name?: string; + + /** + * Base64 encoded name of this asset, as supplied by the creator. + */ + public nameB64?: Uint8Array; + + /** + * (r) Address of account holding reserve (non-minted) units of this asset. + */ + public reserve?: string; + + /** + * (un) Name of a unit of this asset, as supplied by the creator. Included only + * when the name of a unit of this asset is composed of printable utf-8 characters. + */ + public unitName?: string; + + /** + * Base64 encoded name of a unit of this asset, as supplied by the creator. + */ + public unitNameB64?: Uint8Array; + + /** + * (au) URL where more information about the asset can be retrieved. Included only + * when the URL is composed of printable utf-8 characters. + */ + public url?: string; + + /** + * Base64 encoded URL where more information about the asset can be retrieved. + */ + public urlB64?: Uint8Array; + + /** + * Creates a new `AssetParams` object. + * @param creator - The address that created this asset. This is the address where the parameters + * for this asset can be found, and also the address where unwanted asset units can + * be sent in the worst case. + * @param decimals - (dc) The number of digits to use after the decimal point when displaying this + * asset. If 0, the asset is not divisible. If 1, the base unit of the asset is in + * tenths. If 2, the base unit of the asset is in hundredths, and so on. This value + * must be between 0 and 19 (inclusive). + * @param total - (t) The total number of units of this asset. + * @param clawback - (c) Address of account used to clawback holdings of this asset. If empty, + * clawback is not permitted. + * @param defaultFrozen - (df) Whether holdings of this asset are frozen by default. + * @param freeze - (f) Address of account used to freeze holdings of this asset. If empty, freezing + * is not permitted. + * @param manager - (m) Address of account used to manage the keys of this asset and to destroy it. + * @param metadataHash - (am) A commitment to some unspecified asset metadata. The format of this + * metadata is up to the application. + * @param name - (an) Name of this asset, as supplied by the creator. Included only when the + * asset name is composed of printable utf-8 characters. + * @param nameB64 - Base64 encoded name of this asset, as supplied by the creator. + * @param reserve - (r) Address of account holding reserve (non-minted) units of this asset. + * @param unitName - (un) Name of a unit of this asset, as supplied by the creator. Included only + * when the name of a unit of this asset is composed of printable utf-8 characters. + * @param unitNameB64 - Base64 encoded name of a unit of this asset, as supplied by the creator. + * @param url - (au) URL where more information about the asset can be retrieved. Included only + * when the URL is composed of printable utf-8 characters. + * @param urlB64 - Base64 encoded URL where more information about the asset can be retrieved. + */ + constructor({ + creator, + decimals, + total, + clawback, + defaultFrozen, + freeze, + manager, + metadataHash, + name, + nameB64, + reserve, + unitName, + unitNameB64, + url, + urlB64, + }: { + creator: string; + decimals: number | bigint; + total: number | bigint; + clawback?: string; + defaultFrozen?: boolean; + freeze?: string; + manager?: string; + metadataHash?: string | Uint8Array; + name?: string; + nameB64?: string | Uint8Array; + reserve?: string; + unitName?: string; + unitNameB64?: string | Uint8Array; + url?: string; + urlB64?: string | Uint8Array; + }) { + super(); + this.creator = creator; + this.decimals = decimals; + this.total = total; + this.clawback = clawback; + this.defaultFrozen = defaultFrozen; + this.freeze = freeze; + this.manager = manager; + this.metadataHash = + typeof metadataHash === 'string' + ? new Uint8Array(Buffer.from(metadataHash, 'base64')) + : metadataHash; + this.name = name; + this.nameB64 = + typeof nameB64 === 'string' + ? new Uint8Array(Buffer.from(nameB64, 'base64')) + : nameB64; + this.reserve = reserve; + this.unitName = unitName; + this.unitNameB64 = + typeof unitNameB64 === 'string' + ? new Uint8Array(Buffer.from(unitNameB64, 'base64')) + : unitNameB64; + this.url = url; + this.urlB64 = + typeof urlB64 === 'string' + ? new Uint8Array(Buffer.from(urlB64, 'base64')) + : urlB64; + + this.attribute_map = { + creator: 'creator', + decimals: 'decimals', + total: 'total', + clawback: 'clawback', + defaultFrozen: 'default-frozen', + freeze: 'freeze', + manager: 'manager', + metadataHash: 'metadata-hash', + name: 'name', + nameB64: 'name-b64', + reserve: 'reserve', + unitName: 'unit-name', + unitNameB64: 'unit-name-b64', + url: 'url', + urlB64: 'url-b64', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): AssetParams { + /* eslint-disable dot-notation */ + if (typeof data['creator'] === 'undefined') + throw new Error(`Response is missing required field 'creator': ${data}`); + if (typeof data['decimals'] === 'undefined') + throw new Error(`Response is missing required field 'decimals': ${data}`); + if (typeof data['total'] === 'undefined') + throw new Error(`Response is missing required field 'total': ${data}`); + return new AssetParams({ + creator: data['creator'], + decimals: data['decimals'], + total: data['total'], + clawback: data['clawback'], + defaultFrozen: data['default-frozen'], + freeze: data['freeze'], + manager: data['manager'], + metadataHash: data['metadata-hash'], + name: data['name'], + nameB64: data['name-b64'], + reserve: data['reserve'], + unitName: data['unit-name'], + unitNameB64: data['unit-name-b64'], + url: data['url'], + urlB64: data['url-b64'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class AssetResponse extends BaseModel { + /** + * Specifies both the unique identifier and the parameters for an asset + */ + public asset: Asset; + + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + /** + * Creates a new `AssetResponse` object. + * @param asset - Specifies both the unique identifier and the parameters for an asset + * @param currentRound - Round at which the results were computed. + */ + constructor({ + asset, + currentRound, + }: { + asset: Asset; + currentRound: number | bigint; + }) { + super(); + this.asset = asset; + this.currentRound = currentRound; + + this.attribute_map = { + asset: 'asset', + currentRound: 'current-round', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): AssetResponse { + /* eslint-disable dot-notation */ + if (typeof data['asset'] === 'undefined') + throw new Error(`Response is missing required field 'asset': ${data}`); + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + return new AssetResponse({ + asset: Asset.from_obj_for_encoding(data['asset']), + currentRound: data['current-round'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class AssetsResponse extends BaseModel { + public assets: Asset[]; + + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `AssetsResponse` object. + * @param assets - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + assets, + currentRound, + nextToken, + }: { + assets: Asset[]; + currentRound: number | bigint; + nextToken?: string; + }) { + super(); + this.assets = assets; + this.currentRound = currentRound; + this.nextToken = nextToken; + + this.attribute_map = { + assets: 'assets', + currentRound: 'current-round', + nextToken: 'next-token', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): AssetsResponse { + /* eslint-disable dot-notation */ + if (!Array.isArray(data['assets'])) + throw new Error( + `Response is missing required array field 'assets': ${data}` + ); + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + return new AssetsResponse({ + assets: data['assets'].map(Asset.from_obj_for_encoding), + currentRound: data['current-round'], + nextToken: data['next-token'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Block information. + * Definition: + * data/bookkeeping/block.go : Block + */ +export class Block extends BaseModel { + /** + * (gh) hash to which this block belongs. + */ + public genesisHash: Uint8Array; + + /** + * (gen) ID to which this block belongs. + */ + public genesisId: string; + + /** + * (prev) Previous block hash. + */ + public previousBlockHash: Uint8Array; + + /** + * (rnd) Current round on which this block was appended to the chain. + */ + public round: number | bigint; + + /** + * (seed) Sortition seed. + */ + public seed: Uint8Array; + + /** + * (ts) Block creation timestamp in seconds since eposh + */ + public timestamp: number | bigint; + + /** + * (txn) TransactionsRoot authenticates the set of transactions appearing in the + * block. More specifically, it's the root of a merkle tree whose leaves are the + * block's Txids, in lexicographic order. For the empty block, it's 0. Note that + * the TxnRoot does not authenticate the signatures on the transactions, only the + * transactions themselves. Two blocks with the same transactions but in a + * different order and with different signatures will have the same TxnRoot. + */ + public transactionsRoot: Uint8Array; + + /** + * (txn256) TransactionsRootSHA256 is an auxiliary TransactionRoot, built using a + * vector commitment instead of a merkle tree, and SHA256 hash function instead of + * the default SHA512_256. This commitment can be used on environments where only + * the SHA256 function exists. + */ + public transactionsRootSha256: Uint8Array; + + /** + * Participation account data that needs to be checked/acted on by the network. + */ + public participationUpdates?: ParticipationUpdates; + + /** + * Fields relating to rewards, + */ + public rewards?: BlockRewards; + + /** + * Tracks the status of state proofs. + */ + public stateProofTracking?: StateProofTracking[]; + + /** + * (txns) list of transactions corresponding to a given round. + */ + public transactions?: Transaction[]; + + /** + * (tc) TxnCounter counts the number of transactions committed in the ledger, from + * the time at which support for this feature was introduced. + * Specifically, TxnCounter is the number of the next transaction that will be + * committed after this block. It is 0 when no transactions have ever been + * committed (since TxnCounter started being supported). + */ + public txnCounter?: number | bigint; + + /** + * Fields relating to a protocol upgrade. + */ + public upgradeState?: BlockUpgradeState; + + /** + * Fields relating to voting for a protocol upgrade. + */ + public upgradeVote?: BlockUpgradeVote; + + /** + * Creates a new `Block` object. + * @param genesisHash - (gh) hash to which this block belongs. + * @param genesisId - (gen) ID to which this block belongs. + * @param previousBlockHash - (prev) Previous block hash. + * @param round - (rnd) Current round on which this block was appended to the chain. + * @param seed - (seed) Sortition seed. + * @param timestamp - (ts) Block creation timestamp in seconds since eposh + * @param transactionsRoot - (txn) TransactionsRoot authenticates the set of transactions appearing in the + * block. More specifically, it's the root of a merkle tree whose leaves are the + * block's Txids, in lexicographic order. For the empty block, it's 0. Note that + * the TxnRoot does not authenticate the signatures on the transactions, only the + * transactions themselves. Two blocks with the same transactions but in a + * different order and with different signatures will have the same TxnRoot. + * @param transactionsRootSha256 - (txn256) TransactionsRootSHA256 is an auxiliary TransactionRoot, built using a + * vector commitment instead of a merkle tree, and SHA256 hash function instead of + * the default SHA512_256. This commitment can be used on environments where only + * the SHA256 function exists. + * @param participationUpdates - Participation account data that needs to be checked/acted on by the network. + * @param rewards - Fields relating to rewards, + * @param stateProofTracking - Tracks the status of state proofs. + * @param transactions - (txns) list of transactions corresponding to a given round. + * @param txnCounter - (tc) TxnCounter counts the number of transactions committed in the ledger, from + * the time at which support for this feature was introduced. + * Specifically, TxnCounter is the number of the next transaction that will be + * committed after this block. It is 0 when no transactions have ever been + * committed (since TxnCounter started being supported). + * @param upgradeState - Fields relating to a protocol upgrade. + * @param upgradeVote - Fields relating to voting for a protocol upgrade. + */ + constructor({ + genesisHash, + genesisId, + previousBlockHash, + round, + seed, + timestamp, + transactionsRoot, + transactionsRootSha256, + participationUpdates, + rewards, + stateProofTracking, + transactions, + txnCounter, + upgradeState, + upgradeVote, + }: { + genesisHash: string | Uint8Array; + genesisId: string; + previousBlockHash: string | Uint8Array; + round: number | bigint; + seed: string | Uint8Array; + timestamp: number | bigint; + transactionsRoot: string | Uint8Array; + transactionsRootSha256: string | Uint8Array; + participationUpdates?: ParticipationUpdates; + rewards?: BlockRewards; + stateProofTracking?: StateProofTracking[]; + transactions?: Transaction[]; + txnCounter?: number | bigint; + upgradeState?: BlockUpgradeState; + upgradeVote?: BlockUpgradeVote; + }) { + super(); + this.genesisHash = + typeof genesisHash === 'string' + ? new Uint8Array(Buffer.from(genesisHash, 'base64')) + : genesisHash; + this.genesisId = genesisId; + this.previousBlockHash = + typeof previousBlockHash === 'string' + ? new Uint8Array(Buffer.from(previousBlockHash, 'base64')) + : previousBlockHash; + this.round = round; + this.seed = + typeof seed === 'string' + ? new Uint8Array(Buffer.from(seed, 'base64')) + : seed; + this.timestamp = timestamp; + this.transactionsRoot = + typeof transactionsRoot === 'string' + ? new Uint8Array(Buffer.from(transactionsRoot, 'base64')) + : transactionsRoot; + this.transactionsRootSha256 = + typeof transactionsRootSha256 === 'string' + ? new Uint8Array(Buffer.from(transactionsRootSha256, 'base64')) + : transactionsRootSha256; + this.participationUpdates = participationUpdates; + this.rewards = rewards; + this.stateProofTracking = stateProofTracking; + this.transactions = transactions; + this.txnCounter = txnCounter; + this.upgradeState = upgradeState; + this.upgradeVote = upgradeVote; + + this.attribute_map = { + genesisHash: 'genesis-hash', + genesisId: 'genesis-id', + previousBlockHash: 'previous-block-hash', + round: 'round', + seed: 'seed', + timestamp: 'timestamp', + transactionsRoot: 'transactions-root', + transactionsRootSha256: 'transactions-root-sha256', + participationUpdates: 'participation-updates', + rewards: 'rewards', + stateProofTracking: 'state-proof-tracking', + transactions: 'transactions', + txnCounter: 'txn-counter', + upgradeState: 'upgrade-state', + upgradeVote: 'upgrade-vote', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): Block { + /* eslint-disable dot-notation */ + if (typeof data['genesis-hash'] === 'undefined') + throw new Error( + `Response is missing required field 'genesis-hash': ${data}` + ); + if (typeof data['genesis-id'] === 'undefined') + throw new Error( + `Response is missing required field 'genesis-id': ${data}` + ); + if (typeof data['previous-block-hash'] === 'undefined') + throw new Error( + `Response is missing required field 'previous-block-hash': ${data}` + ); + if (typeof data['round'] === 'undefined') + throw new Error(`Response is missing required field 'round': ${data}`); + if (typeof data['seed'] === 'undefined') + throw new Error(`Response is missing required field 'seed': ${data}`); + if (typeof data['timestamp'] === 'undefined') + throw new Error( + `Response is missing required field 'timestamp': ${data}` + ); + if (typeof data['transactions-root'] === 'undefined') + throw new Error( + `Response is missing required field 'transactions-root': ${data}` + ); + if (typeof data['transactions-root-sha256'] === 'undefined') + throw new Error( + `Response is missing required field 'transactions-root-sha256': ${data}` + ); + return new Block({ + genesisHash: data['genesis-hash'], + genesisId: data['genesis-id'], + previousBlockHash: data['previous-block-hash'], + round: data['round'], + seed: data['seed'], + timestamp: data['timestamp'], + transactionsRoot: data['transactions-root'], + transactionsRootSha256: data['transactions-root-sha256'], + participationUpdates: + typeof data['participation-updates'] !== 'undefined' + ? ParticipationUpdates.from_obj_for_encoding( + data['participation-updates'] + ) + : undefined, + rewards: + typeof data['rewards'] !== 'undefined' + ? BlockRewards.from_obj_for_encoding(data['rewards']) + : undefined, + stateProofTracking: + typeof data['state-proof-tracking'] !== 'undefined' + ? data['state-proof-tracking'].map( + StateProofTracking.from_obj_for_encoding + ) + : undefined, + transactions: + typeof data['transactions'] !== 'undefined' + ? data['transactions'].map(Transaction.from_obj_for_encoding) + : undefined, + txnCounter: data['txn-counter'], + upgradeState: + typeof data['upgrade-state'] !== 'undefined' + ? BlockUpgradeState.from_obj_for_encoding(data['upgrade-state']) + : undefined, + upgradeVote: + typeof data['upgrade-vote'] !== 'undefined' + ? BlockUpgradeVote.from_obj_for_encoding(data['upgrade-vote']) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Fields relating to rewards, + */ +export class BlockRewards extends BaseModel { + /** + * (fees) accepts transaction fees, it can only spend to the incentive pool. + */ + public feeSink: string; + + /** + * (rwcalr) number of leftover MicroAlgos after the distribution of rewards-rate + * MicroAlgos for every reward unit in the next round. + */ + public rewardsCalculationRound: number | bigint; + + /** + * (earn) How many rewards, in MicroAlgos, have been distributed to each RewardUnit + * of MicroAlgos since genesis. + */ + public rewardsLevel: number | bigint; + + /** + * (rwd) accepts periodic injections from the fee-sink and continually + * redistributes them as rewards. + */ + public rewardsPool: string; + + /** + * (rate) Number of new MicroAlgos added to the participation stake from rewards at + * the next round. + */ + public rewardsRate: number | bigint; + + /** + * (frac) Number of leftover MicroAlgos after the distribution of + * RewardsRate/rewardUnits MicroAlgos for every reward unit in the next round. + */ + public rewardsResidue: number | bigint; + + /** + * Creates a new `BlockRewards` object. + * @param feeSink - (fees) accepts transaction fees, it can only spend to the incentive pool. + * @param rewardsCalculationRound - (rwcalr) number of leftover MicroAlgos after the distribution of rewards-rate + * MicroAlgos for every reward unit in the next round. + * @param rewardsLevel - (earn) How many rewards, in MicroAlgos, have been distributed to each RewardUnit + * of MicroAlgos since genesis. + * @param rewardsPool - (rwd) accepts periodic injections from the fee-sink and continually + * redistributes them as rewards. + * @param rewardsRate - (rate) Number of new MicroAlgos added to the participation stake from rewards at + * the next round. + * @param rewardsResidue - (frac) Number of leftover MicroAlgos after the distribution of + * RewardsRate/rewardUnits MicroAlgos for every reward unit in the next round. + */ + constructor({ + feeSink, + rewardsCalculationRound, + rewardsLevel, + rewardsPool, + rewardsRate, + rewardsResidue, + }: { + feeSink: string; + rewardsCalculationRound: number | bigint; + rewardsLevel: number | bigint; + rewardsPool: string; + rewardsRate: number | bigint; + rewardsResidue: number | bigint; + }) { + super(); + this.feeSink = feeSink; + this.rewardsCalculationRound = rewardsCalculationRound; + this.rewardsLevel = rewardsLevel; + this.rewardsPool = rewardsPool; + this.rewardsRate = rewardsRate; + this.rewardsResidue = rewardsResidue; + + this.attribute_map = { + feeSink: 'fee-sink', + rewardsCalculationRound: 'rewards-calculation-round', + rewardsLevel: 'rewards-level', + rewardsPool: 'rewards-pool', + rewardsRate: 'rewards-rate', + rewardsResidue: 'rewards-residue', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): BlockRewards { + /* eslint-disable dot-notation */ + if (typeof data['fee-sink'] === 'undefined') + throw new Error(`Response is missing required field 'fee-sink': ${data}`); + if (typeof data['rewards-calculation-round'] === 'undefined') + throw new Error( + `Response is missing required field 'rewards-calculation-round': ${data}` + ); + if (typeof data['rewards-level'] === 'undefined') + throw new Error( + `Response is missing required field 'rewards-level': ${data}` + ); + if (typeof data['rewards-pool'] === 'undefined') + throw new Error( + `Response is missing required field 'rewards-pool': ${data}` + ); + if (typeof data['rewards-rate'] === 'undefined') + throw new Error( + `Response is missing required field 'rewards-rate': ${data}` + ); + if (typeof data['rewards-residue'] === 'undefined') + throw new Error( + `Response is missing required field 'rewards-residue': ${data}` + ); + return new BlockRewards({ + feeSink: data['fee-sink'], + rewardsCalculationRound: data['rewards-calculation-round'], + rewardsLevel: data['rewards-level'], + rewardsPool: data['rewards-pool'], + rewardsRate: data['rewards-rate'], + rewardsResidue: data['rewards-residue'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Fields relating to a protocol upgrade. + */ +export class BlockUpgradeState extends BaseModel { + /** + * (proto) The current protocol version. + */ + public currentProtocol: string; + + /** + * (nextproto) The next proposed protocol version. + */ + public nextProtocol?: string; + + /** + * (nextyes) Number of blocks which approved the protocol upgrade. + */ + public nextProtocolApprovals?: number | bigint; + + /** + * (nextswitch) Round on which the protocol upgrade will take effect. + */ + public nextProtocolSwitchOn?: number | bigint; + + /** + * (nextbefore) Deadline round for this protocol upgrade (No votes will be consider + * after this round). + */ + public nextProtocolVoteBefore?: number | bigint; + + /** + * Creates a new `BlockUpgradeState` object. + * @param currentProtocol - (proto) The current protocol version. + * @param nextProtocol - (nextproto) The next proposed protocol version. + * @param nextProtocolApprovals - (nextyes) Number of blocks which approved the protocol upgrade. + * @param nextProtocolSwitchOn - (nextswitch) Round on which the protocol upgrade will take effect. + * @param nextProtocolVoteBefore - (nextbefore) Deadline round for this protocol upgrade (No votes will be consider + * after this round). + */ + constructor({ + currentProtocol, + nextProtocol, + nextProtocolApprovals, + nextProtocolSwitchOn, + nextProtocolVoteBefore, + }: { + currentProtocol: string; + nextProtocol?: string; + nextProtocolApprovals?: number | bigint; + nextProtocolSwitchOn?: number | bigint; + nextProtocolVoteBefore?: number | bigint; + }) { + super(); + this.currentProtocol = currentProtocol; + this.nextProtocol = nextProtocol; + this.nextProtocolApprovals = nextProtocolApprovals; + this.nextProtocolSwitchOn = nextProtocolSwitchOn; + this.nextProtocolVoteBefore = nextProtocolVoteBefore; + + this.attribute_map = { + currentProtocol: 'current-protocol', + nextProtocol: 'next-protocol', + nextProtocolApprovals: 'next-protocol-approvals', + nextProtocolSwitchOn: 'next-protocol-switch-on', + nextProtocolVoteBefore: 'next-protocol-vote-before', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): BlockUpgradeState { + /* eslint-disable dot-notation */ + if (typeof data['current-protocol'] === 'undefined') + throw new Error( + `Response is missing required field 'current-protocol': ${data}` + ); + return new BlockUpgradeState({ + currentProtocol: data['current-protocol'], + nextProtocol: data['next-protocol'], + nextProtocolApprovals: data['next-protocol-approvals'], + nextProtocolSwitchOn: data['next-protocol-switch-on'], + nextProtocolVoteBefore: data['next-protocol-vote-before'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Fields relating to voting for a protocol upgrade. + */ +export class BlockUpgradeVote extends BaseModel { + /** + * (upgradeyes) Indicates a yes vote for the current proposal. + */ + public upgradeApprove?: boolean; + + /** + * (upgradedelay) Indicates the time between acceptance and execution. + */ + public upgradeDelay?: number | bigint; + + /** + * (upgradeprop) Indicates a proposed upgrade. + */ + public upgradePropose?: string; + + /** + * Creates a new `BlockUpgradeVote` object. + * @param upgradeApprove - (upgradeyes) Indicates a yes vote for the current proposal. + * @param upgradeDelay - (upgradedelay) Indicates the time between acceptance and execution. + * @param upgradePropose - (upgradeprop) Indicates a proposed upgrade. + */ + constructor({ + upgradeApprove, + upgradeDelay, + upgradePropose, + }: { + upgradeApprove?: boolean; + upgradeDelay?: number | bigint; + upgradePropose?: string; + }) { + super(); + this.upgradeApprove = upgradeApprove; + this.upgradeDelay = upgradeDelay; + this.upgradePropose = upgradePropose; + + this.attribute_map = { + upgradeApprove: 'upgrade-approve', + upgradeDelay: 'upgrade-delay', + upgradePropose: 'upgrade-propose', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): BlockUpgradeVote { + /* eslint-disable dot-notation */ + return new BlockUpgradeVote({ + upgradeApprove: data['upgrade-approve'], + upgradeDelay: data['upgrade-delay'], + upgradePropose: data['upgrade-propose'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Box name and its content. + */ +export class Box extends BaseModel { + /** + * (name) box name, base64 encoded + */ + public name: Uint8Array; + + /** + * (value) box value, base64 encoded. + */ + public value: Uint8Array; + + /** + * Creates a new `Box` object. + * @param name - (name) box name, base64 encoded + * @param value - (value) box value, base64 encoded. + */ + constructor({ + name, + value, + }: { + name: string | Uint8Array; + value: string | Uint8Array; + }) { + super(); + this.name = + typeof name === 'string' + ? new Uint8Array(Buffer.from(name, 'base64')) + : name; + this.value = + typeof value === 'string' + ? new Uint8Array(Buffer.from(value, 'base64')) + : value; + + this.attribute_map = { + name: 'name', + value: 'value', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): Box { + /* eslint-disable dot-notation */ + if (typeof data['name'] === 'undefined') + throw new Error(`Response is missing required field 'name': ${data}`); + if (typeof data['value'] === 'undefined') + throw new Error(`Response is missing required field 'value': ${data}`); + return new Box({ + name: data['name'], + value: data['value'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Box descriptor describes an app box without a value. + */ +export class BoxDescriptor extends BaseModel { + /** + * Base64 encoded box name + */ + public name: Uint8Array; + + /** + * Creates a new `BoxDescriptor` object. + * @param name - Base64 encoded box name + */ + constructor({ name }: { name: string | Uint8Array }) { + super(); + this.name = + typeof name === 'string' + ? new Uint8Array(Buffer.from(name, 'base64')) + : name; + + this.attribute_map = { + name: 'name', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): BoxDescriptor { + /* eslint-disable dot-notation */ + if (typeof data['name'] === 'undefined') + throw new Error(`Response is missing required field 'name': ${data}`); + return new BoxDescriptor({ + name: data['name'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Box names of an application + */ +export class BoxesResponse extends BaseModel { + /** + * (appidx) application index. + */ + public applicationId: number | bigint; + + public boxes: BoxDescriptor[]; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `BoxesResponse` object. + * @param applicationId - (appidx) application index. + * @param boxes - + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + applicationId, + boxes, + nextToken, + }: { + applicationId: number | bigint; + boxes: BoxDescriptor[]; + nextToken?: string; + }) { + super(); + this.applicationId = applicationId; + this.boxes = boxes; + this.nextToken = nextToken; + + this.attribute_map = { + applicationId: 'application-id', + boxes: 'boxes', + nextToken: 'next-token', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): BoxesResponse { + /* eslint-disable dot-notation */ + if (typeof data['application-id'] === 'undefined') + throw new Error( + `Response is missing required field 'application-id': ${data}` + ); + if (!Array.isArray(data['boxes'])) + throw new Error( + `Response is missing required array field 'boxes': ${data}` + ); + return new BoxesResponse({ + applicationId: data['application-id'], + boxes: data['boxes'].map(BoxDescriptor.from_obj_for_encoding), + nextToken: data['next-token'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Response for errors + */ +export class ErrorResponse extends BaseModel { + public message: string; + + public data?: Record; + + /** + * Creates a new `ErrorResponse` object. + * @param message - + * @param data - + */ + constructor({ + message, + data, + }: { + message: string; + data?: Record; + }) { + super(); + this.message = message; + this.data = data; + + this.attribute_map = { + message: 'message', + data: 'data', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): ErrorResponse { + /* eslint-disable dot-notation */ + if (typeof data['message'] === 'undefined') + throw new Error(`Response is missing required field 'message': ${data}`); + return new ErrorResponse({ + message: data['message'], + data: data['data'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Represents a TEAL value delta. + */ +export class EvalDelta extends BaseModel { + /** + * (at) delta action. + */ + public action: number | bigint; + + /** + * (bs) bytes value. + */ + public bytes?: string; + + /** + * (ui) uint value. + */ + public uint?: number | bigint; + + /** + * Creates a new `EvalDelta` object. + * @param action - (at) delta action. + * @param bytes - (bs) bytes value. + * @param uint - (ui) uint value. + */ + constructor({ + action, + bytes, + uint, + }: { + action: number | bigint; + bytes?: string; + uint?: number | bigint; + }) { + super(); + this.action = action; + this.bytes = bytes; + this.uint = uint; + + this.attribute_map = { + action: 'action', + bytes: 'bytes', + uint: 'uint', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): EvalDelta { + /* eslint-disable dot-notation */ + if (typeof data['action'] === 'undefined') + throw new Error(`Response is missing required field 'action': ${data}`); + return new EvalDelta({ + action: data['action'], + bytes: data['bytes'], + uint: data['uint'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Key-value pairs for StateDelta. + */ +export class EvalDeltaKeyValue extends BaseModel { + public key: string; + + /** + * Represents a TEAL value delta. + */ + public value: EvalDelta; + + /** + * Creates a new `EvalDeltaKeyValue` object. + * @param key - + * @param value - Represents a TEAL value delta. + */ + constructor({ key, value }: { key: string; value: EvalDelta }) { + super(); + this.key = key; + this.value = value; + + this.attribute_map = { + key: 'key', + value: 'value', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): EvalDeltaKeyValue { + /* eslint-disable dot-notation */ + if (typeof data['key'] === 'undefined') + throw new Error(`Response is missing required field 'key': ${data}`); + if (typeof data['value'] === 'undefined') + throw new Error(`Response is missing required field 'value': ${data}`); + return new EvalDeltaKeyValue({ + key: data['key'], + value: EvalDelta.from_obj_for_encoding(data['value']), + }); + /* eslint-enable dot-notation */ + } +} + +export class HashFactory extends BaseModel { + /** + * (t) + */ + public hashType?: number | bigint; + + /** + * Creates a new `HashFactory` object. + * @param hashType - (t) + */ + constructor({ hashType }: { hashType?: number | bigint }) { + super(); + this.hashType = hashType; + + this.attribute_map = { + hashType: 'hash-type', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): HashFactory { + /* eslint-disable dot-notation */ + return new HashFactory({ + hashType: data['hash-type'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * A health check response. + */ +export class HealthCheck extends BaseModel { + public dbAvailable: boolean; + + public isMigrating: boolean; + + public message: string; + + public round: number | bigint; + + /** + * Current version. + */ + public version: string; + + public data?: Record; + + public errors?: string[]; + + /** + * Creates a new `HealthCheck` object. + * @param dbAvailable - + * @param isMigrating - + * @param message - + * @param round - + * @param version - Current version. + * @param data - + * @param errors - + */ + constructor({ + dbAvailable, + isMigrating, + message, + round, + version, + data, + errors, + }: { + dbAvailable: boolean; + isMigrating: boolean; + message: string; + round: number | bigint; + version: string; + data?: Record; + errors?: string[]; + }) { + super(); + this.dbAvailable = dbAvailable; + this.isMigrating = isMigrating; + this.message = message; + this.round = round; + this.version = version; + this.data = data; + this.errors = errors; + + this.attribute_map = { + dbAvailable: 'db-available', + isMigrating: 'is-migrating', + message: 'message', + round: 'round', + version: 'version', + data: 'data', + errors: 'errors', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): HealthCheck { + /* eslint-disable dot-notation */ + if (typeof data['db-available'] === 'undefined') + throw new Error( + `Response is missing required field 'db-available': ${data}` + ); + if (typeof data['is-migrating'] === 'undefined') + throw new Error( + `Response is missing required field 'is-migrating': ${data}` + ); + if (typeof data['message'] === 'undefined') + throw new Error(`Response is missing required field 'message': ${data}`); + if (typeof data['round'] === 'undefined') + throw new Error(`Response is missing required field 'round': ${data}`); + if (typeof data['version'] === 'undefined') + throw new Error(`Response is missing required field 'version': ${data}`); + return new HealthCheck({ + dbAvailable: data['db-available'], + isMigrating: data['is-migrating'], + message: data['message'], + round: data['round'], + version: data['version'], + data: data['data'], + errors: data['errors'], + }); + /* eslint-enable dot-notation */ + } +} + +export class IndexerStateProofMessage extends BaseModel { + /** + * (b) + */ + public blockHeadersCommitment?: Uint8Array; + + /** + * (f) + */ + public firstAttestedRound?: number | bigint; + + /** + * (l) + */ + public latestAttestedRound?: number | bigint; + + /** + * (P) + */ + public lnProvenWeight?: number | bigint; + + /** + * (v) + */ + public votersCommitment?: Uint8Array; + + /** + * Creates a new `IndexerStateProofMessage` object. + * @param blockHeadersCommitment - (b) + * @param firstAttestedRound - (f) + * @param latestAttestedRound - (l) + * @param lnProvenWeight - (P) + * @param votersCommitment - (v) + */ + constructor({ + blockHeadersCommitment, + firstAttestedRound, + latestAttestedRound, + lnProvenWeight, + votersCommitment, + }: { + blockHeadersCommitment?: string | Uint8Array; + firstAttestedRound?: number | bigint; + latestAttestedRound?: number | bigint; + lnProvenWeight?: number | bigint; + votersCommitment?: string | Uint8Array; + }) { + super(); + this.blockHeadersCommitment = + typeof blockHeadersCommitment === 'string' + ? new Uint8Array(Buffer.from(blockHeadersCommitment, 'base64')) + : blockHeadersCommitment; + this.firstAttestedRound = firstAttestedRound; + this.latestAttestedRound = latestAttestedRound; + this.lnProvenWeight = lnProvenWeight; + this.votersCommitment = + typeof votersCommitment === 'string' + ? new Uint8Array(Buffer.from(votersCommitment, 'base64')) + : votersCommitment; + + this.attribute_map = { + blockHeadersCommitment: 'block-headers-commitment', + firstAttestedRound: 'first-attested-round', + latestAttestedRound: 'latest-attested-round', + lnProvenWeight: 'ln-proven-weight', + votersCommitment: 'voters-commitment', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): IndexerStateProofMessage { + /* eslint-disable dot-notation */ + return new IndexerStateProofMessage({ + blockHeadersCommitment: data['block-headers-commitment'], + firstAttestedRound: data['first-attested-round'], + latestAttestedRound: data['latest-attested-round'], + lnProvenWeight: data['ln-proven-weight'], + votersCommitment: data['voters-commitment'], + }); + /* eslint-enable dot-notation */ + } +} + +export class MerkleArrayProof extends BaseModel { + public hashFactory?: HashFactory; + + /** + * (pth) + */ + public path?: Uint8Array[]; + + /** + * (td) + */ + public treeDepth?: number | bigint; + + /** + * Creates a new `MerkleArrayProof` object. + * @param hashFactory - + * @param path - (pth) + * @param treeDepth - (td) + */ + constructor({ + hashFactory, + path, + treeDepth, + }: { + hashFactory?: HashFactory; + path?: Uint8Array[]; + treeDepth?: number | bigint; + }) { + super(); + this.hashFactory = hashFactory; + this.path = path; + this.treeDepth = treeDepth; + + this.attribute_map = { + hashFactory: 'hash-factory', + path: 'path', + treeDepth: 'tree-depth', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): MerkleArrayProof { + /* eslint-disable dot-notation */ + return new MerkleArrayProof({ + hashFactory: + typeof data['hash-factory'] !== 'undefined' + ? HashFactory.from_obj_for_encoding(data['hash-factory']) + : undefined, + path: data['path'], + treeDepth: data['tree-depth'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * A simplified version of AssetHolding + */ +export class MiniAssetHolding extends BaseModel { + public address: string; + + public amount: number | bigint; + + public isFrozen: boolean; + + /** + * Whether or not this asset holding is currently deleted from its account. + */ + public deleted?: boolean; + + /** + * Round during which the account opted into the asset. + */ + public optedInAtRound?: number | bigint; + + /** + * Round during which the account opted out of the asset. + */ + public optedOutAtRound?: number | bigint; + + /** + * Creates a new `MiniAssetHolding` object. + * @param address - + * @param amount - + * @param isFrozen - + * @param deleted - Whether or not this asset holding is currently deleted from its account. + * @param optedInAtRound - Round during which the account opted into the asset. + * @param optedOutAtRound - Round during which the account opted out of the asset. + */ + constructor({ + address, + amount, + isFrozen, + deleted, + optedInAtRound, + optedOutAtRound, + }: { + address: string; + amount: number | bigint; + isFrozen: boolean; + deleted?: boolean; + optedInAtRound?: number | bigint; + optedOutAtRound?: number | bigint; + }) { + super(); + this.address = address; + this.amount = amount; + this.isFrozen = isFrozen; + this.deleted = deleted; + this.optedInAtRound = optedInAtRound; + this.optedOutAtRound = optedOutAtRound; + + this.attribute_map = { + address: 'address', + amount: 'amount', + isFrozen: 'is-frozen', + deleted: 'deleted', + optedInAtRound: 'opted-in-at-round', + optedOutAtRound: 'opted-out-at-round', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): MiniAssetHolding { + /* eslint-disable dot-notation */ + if (typeof data['address'] === 'undefined') + throw new Error(`Response is missing required field 'address': ${data}`); + if (typeof data['amount'] === 'undefined') + throw new Error(`Response is missing required field 'amount': ${data}`); + if (typeof data['is-frozen'] === 'undefined') + throw new Error( + `Response is missing required field 'is-frozen': ${data}` + ); + return new MiniAssetHolding({ + address: data['address'], + amount: data['amount'], + isFrozen: data['is-frozen'], + deleted: data['deleted'], + optedInAtRound: data['opted-in-at-round'], + optedOutAtRound: data['opted-out-at-round'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Participation account data that needs to be checked/acted on by the network. + */ +export class ParticipationUpdates extends BaseModel { + /** + * (partupdrmv) a list of online accounts that needs to be converted to offline + * since their participation key expired. + */ + public expiredParticipationAccounts?: string[]; + + /** + * Creates a new `ParticipationUpdates` object. + * @param expiredParticipationAccounts - (partupdrmv) a list of online accounts that needs to be converted to offline + * since their participation key expired. + */ + constructor({ + expiredParticipationAccounts, + }: { + expiredParticipationAccounts?: string[]; + }) { + super(); + this.expiredParticipationAccounts = expiredParticipationAccounts; + + this.attribute_map = { + expiredParticipationAccounts: 'expired-participation-accounts', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): ParticipationUpdates { + /* eslint-disable dot-notation */ + return new ParticipationUpdates({ + expiredParticipationAccounts: data['expired-participation-accounts'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * (sp) represents a state proof. + * Definition: + * crypto/stateproof/structs.go : StateProof + */ +export class StateProofFields extends BaseModel { + /** + * (P) + */ + public partProofs?: MerkleArrayProof; + + /** + * (pr) Sequence of reveal positions. + */ + public positionsToReveal?: (number | bigint)[]; + + /** + * (r) Note that this is actually stored as a map[uint64] - Reveal in the actual + * msgp + */ + public reveals?: StateProofReveal[]; + + /** + * (v) Salt version of the merkle signature. + */ + public saltVersion?: number | bigint; + + /** + * (c) + */ + public sigCommit?: Uint8Array; + + /** + * (S) + */ + public sigProofs?: MerkleArrayProof; + + /** + * (w) + */ + public signedWeight?: number | bigint; + + /** + * Creates a new `StateProofFields` object. + * @param partProofs - (P) + * @param positionsToReveal - (pr) Sequence of reveal positions. + * @param reveals - (r) Note that this is actually stored as a map[uint64] - Reveal in the actual + * msgp + * @param saltVersion - (v) Salt version of the merkle signature. + * @param sigCommit - (c) + * @param sigProofs - (S) + * @param signedWeight - (w) + */ + constructor({ + partProofs, + positionsToReveal, + reveals, + saltVersion, + sigCommit, + sigProofs, + signedWeight, + }: { + partProofs?: MerkleArrayProof; + positionsToReveal?: (number | bigint)[]; + reveals?: StateProofReveal[]; + saltVersion?: number | bigint; + sigCommit?: string | Uint8Array; + sigProofs?: MerkleArrayProof; + signedWeight?: number | bigint; + }) { + super(); + this.partProofs = partProofs; + this.positionsToReveal = positionsToReveal; + this.reveals = reveals; + this.saltVersion = saltVersion; + this.sigCommit = + typeof sigCommit === 'string' + ? new Uint8Array(Buffer.from(sigCommit, 'base64')) + : sigCommit; + this.sigProofs = sigProofs; + this.signedWeight = signedWeight; + + this.attribute_map = { + partProofs: 'part-proofs', + positionsToReveal: 'positions-to-reveal', + reveals: 'reveals', + saltVersion: 'salt-version', + sigCommit: 'sig-commit', + sigProofs: 'sig-proofs', + signedWeight: 'signed-weight', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): StateProofFields { + /* eslint-disable dot-notation */ + return new StateProofFields({ + partProofs: + typeof data['part-proofs'] !== 'undefined' + ? MerkleArrayProof.from_obj_for_encoding(data['part-proofs']) + : undefined, + positionsToReveal: data['positions-to-reveal'], + reveals: + typeof data['reveals'] !== 'undefined' + ? data['reveals'].map(StateProofReveal.from_obj_for_encoding) + : undefined, + saltVersion: data['salt-version'], + sigCommit: data['sig-commit'], + sigProofs: + typeof data['sig-proofs'] !== 'undefined' + ? MerkleArrayProof.from_obj_for_encoding(data['sig-proofs']) + : undefined, + signedWeight: data['signed-weight'], + }); + /* eslint-enable dot-notation */ + } +} + +export class StateProofParticipant extends BaseModel { + /** + * (p) + */ + public verifier?: StateProofVerifier; + + /** + * (w) + */ + public weight?: number | bigint; + + /** + * Creates a new `StateProofParticipant` object. + * @param verifier - (p) + * @param weight - (w) + */ + constructor({ + verifier, + weight, + }: { + verifier?: StateProofVerifier; + weight?: number | bigint; + }) { + super(); + this.verifier = verifier; + this.weight = weight; + + this.attribute_map = { + verifier: 'verifier', + weight: 'weight', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): StateProofParticipant { + /* eslint-disable dot-notation */ + return new StateProofParticipant({ + verifier: + typeof data['verifier'] !== 'undefined' + ? StateProofVerifier.from_obj_for_encoding(data['verifier']) + : undefined, + weight: data['weight'], + }); + /* eslint-enable dot-notation */ + } +} + +export class StateProofReveal extends BaseModel { + /** + * (p) + */ + public participant?: StateProofParticipant; + + /** + * The position in the signature and participants arrays corresponding to this + * entry. + */ + public position?: number | bigint; + + /** + * (s) + */ + public sigSlot?: StateProofSigSlot; + + /** + * Creates a new `StateProofReveal` object. + * @param participant - (p) + * @param position - The position in the signature and participants arrays corresponding to this + * entry. + * @param sigSlot - (s) + */ + constructor({ + participant, + position, + sigSlot, + }: { + participant?: StateProofParticipant; + position?: number | bigint; + sigSlot?: StateProofSigSlot; + }) { + super(); + this.participant = participant; + this.position = position; + this.sigSlot = sigSlot; + + this.attribute_map = { + participant: 'participant', + position: 'position', + sigSlot: 'sig-slot', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): StateProofReveal { + /* eslint-disable dot-notation */ + return new StateProofReveal({ + participant: + typeof data['participant'] !== 'undefined' + ? StateProofParticipant.from_obj_for_encoding(data['participant']) + : undefined, + position: data['position'], + sigSlot: + typeof data['sig-slot'] !== 'undefined' + ? StateProofSigSlot.from_obj_for_encoding(data['sig-slot']) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +export class StateProofSigSlot extends BaseModel { + /** + * (l) The total weight of signatures in the lower-numbered slots. + */ + public lowerSigWeight?: number | bigint; + + public signature?: StateProofSignature; + + /** + * Creates a new `StateProofSigSlot` object. + * @param lowerSigWeight - (l) The total weight of signatures in the lower-numbered slots. + * @param signature - + */ + constructor({ + lowerSigWeight, + signature, + }: { + lowerSigWeight?: number | bigint; + signature?: StateProofSignature; + }) { + super(); + this.lowerSigWeight = lowerSigWeight; + this.signature = signature; + + this.attribute_map = { + lowerSigWeight: 'lower-sig-weight', + signature: 'signature', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): StateProofSigSlot { + /* eslint-disable dot-notation */ + return new StateProofSigSlot({ + lowerSigWeight: data['lower-sig-weight'], + signature: + typeof data['signature'] !== 'undefined' + ? StateProofSignature.from_obj_for_encoding(data['signature']) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +export class StateProofSignature extends BaseModel { + public falconSignature?: Uint8Array; + + public merkleArrayIndex?: number | bigint; + + public proof?: MerkleArrayProof; + + /** + * (vkey) + */ + public verifyingKey?: Uint8Array; + + /** + * Creates a new `StateProofSignature` object. + * @param falconSignature - + * @param merkleArrayIndex - + * @param proof - + * @param verifyingKey - (vkey) + */ + constructor({ + falconSignature, + merkleArrayIndex, + proof, + verifyingKey, + }: { + falconSignature?: string | Uint8Array; + merkleArrayIndex?: number | bigint; + proof?: MerkleArrayProof; + verifyingKey?: string | Uint8Array; + }) { + super(); + this.falconSignature = + typeof falconSignature === 'string' + ? new Uint8Array(Buffer.from(falconSignature, 'base64')) + : falconSignature; + this.merkleArrayIndex = merkleArrayIndex; + this.proof = proof; + this.verifyingKey = + typeof verifyingKey === 'string' + ? new Uint8Array(Buffer.from(verifyingKey, 'base64')) + : verifyingKey; + + this.attribute_map = { + falconSignature: 'falcon-signature', + merkleArrayIndex: 'merkle-array-index', + proof: 'proof', + verifyingKey: 'verifying-key', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): StateProofSignature { + /* eslint-disable dot-notation */ + return new StateProofSignature({ + falconSignature: data['falcon-signature'], + merkleArrayIndex: data['merkle-array-index'], + proof: + typeof data['proof'] !== 'undefined' + ? MerkleArrayProof.from_obj_for_encoding(data['proof']) + : undefined, + verifyingKey: data['verifying-key'], + }); + /* eslint-enable dot-notation */ + } +} + +export class StateProofTracking extends BaseModel { + /** + * (n) Next round for which we will accept a state proof transaction. + */ + public nextRound?: number | bigint; + + /** + * (t) The total number of microalgos held by the online accounts during the + * StateProof round. + */ + public onlineTotalWeight?: number | bigint; + + /** + * State Proof Type. Note the raw object uses map with this as key. + */ + public type?: number | bigint; + + /** + * (v) Root of a vector commitment containing online accounts that will help sign + * the proof. + */ + public votersCommitment?: Uint8Array; + + /** + * Creates a new `StateProofTracking` object. + * @param nextRound - (n) Next round for which we will accept a state proof transaction. + * @param onlineTotalWeight - (t) The total number of microalgos held by the online accounts during the + * StateProof round. + * @param type - State Proof Type. Note the raw object uses map with this as key. + * @param votersCommitment - (v) Root of a vector commitment containing online accounts that will help sign + * the proof. + */ + constructor({ + nextRound, + onlineTotalWeight, + type, + votersCommitment, + }: { + nextRound?: number | bigint; + onlineTotalWeight?: number | bigint; + type?: number | bigint; + votersCommitment?: string | Uint8Array; + }) { + super(); + this.nextRound = nextRound; + this.onlineTotalWeight = onlineTotalWeight; + this.type = type; + this.votersCommitment = + typeof votersCommitment === 'string' + ? new Uint8Array(Buffer.from(votersCommitment, 'base64')) + : votersCommitment; + + this.attribute_map = { + nextRound: 'next-round', + onlineTotalWeight: 'online-total-weight', + type: 'type', + votersCommitment: 'voters-commitment', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): StateProofTracking { + /* eslint-disable dot-notation */ + return new StateProofTracking({ + nextRound: data['next-round'], + onlineTotalWeight: data['online-total-weight'], + type: data['type'], + votersCommitment: data['voters-commitment'], + }); + /* eslint-enable dot-notation */ + } +} + +export class StateProofVerifier extends BaseModel { + /** + * (cmt) Represents the root of the vector commitment tree. + */ + public commitment?: Uint8Array; + + /** + * (lf) Key lifetime. + */ + public keyLifetime?: number | bigint; + + /** + * Creates a new `StateProofVerifier` object. + * @param commitment - (cmt) Represents the root of the vector commitment tree. + * @param keyLifetime - (lf) Key lifetime. + */ + constructor({ + commitment, + keyLifetime, + }: { + commitment?: string | Uint8Array; + keyLifetime?: number | bigint; + }) { + super(); + this.commitment = + typeof commitment === 'string' + ? new Uint8Array(Buffer.from(commitment, 'base64')) + : commitment; + this.keyLifetime = keyLifetime; + + this.attribute_map = { + commitment: 'commitment', + keyLifetime: 'key-lifetime', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): StateProofVerifier { + /* eslint-disable dot-notation */ + return new StateProofVerifier({ + commitment: data['commitment'], + keyLifetime: data['key-lifetime'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Represents a (apls) local-state or (apgs) global-state schema. These schemas + * determine how much storage may be used in a local-state or global-state for an + * application. The more space used, the larger minimum balance must be maintained + * in the account holding the data. + */ +export class StateSchema extends BaseModel { + /** + * Maximum number of TEAL byte slices that may be stored in the key/value store. + */ + public numByteSlice: number | bigint; + + /** + * Maximum number of TEAL uints that may be stored in the key/value store. + */ + public numUint: number | bigint; + + /** + * Creates a new `StateSchema` object. + * @param numByteSlice - Maximum number of TEAL byte slices that may be stored in the key/value store. + * @param numUint - Maximum number of TEAL uints that may be stored in the key/value store. + */ + constructor({ + numByteSlice, + numUint, + }: { + numByteSlice: number | bigint; + numUint: number | bigint; + }) { + super(); + this.numByteSlice = numByteSlice; + this.numUint = numUint; + + this.attribute_map = { + numByteSlice: 'num-byte-slice', + numUint: 'num-uint', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): StateSchema { + /* eslint-disable dot-notation */ + if (typeof data['num-byte-slice'] === 'undefined') + throw new Error( + `Response is missing required field 'num-byte-slice': ${data}` + ); + if (typeof data['num-uint'] === 'undefined') + throw new Error(`Response is missing required field 'num-uint': ${data}`); + return new StateSchema({ + numByteSlice: data['num-byte-slice'], + numUint: data['num-uint'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Represents a key-value pair in an application store. + */ +export class TealKeyValue extends BaseModel { + public key: string; + + /** + * Represents a TEAL value. + */ + public value: TealValue; + + /** + * Creates a new `TealKeyValue` object. + * @param key - + * @param value - Represents a TEAL value. + */ + constructor({ key, value }: { key: string; value: TealValue }) { + super(); + this.key = key; + this.value = value; + + this.attribute_map = { + key: 'key', + value: 'value', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): TealKeyValue { + /* eslint-disable dot-notation */ + if (typeof data['key'] === 'undefined') + throw new Error(`Response is missing required field 'key': ${data}`); + if (typeof data['value'] === 'undefined') + throw new Error(`Response is missing required field 'value': ${data}`); + return new TealKeyValue({ + key: data['key'], + value: TealValue.from_obj_for_encoding(data['value']), + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Represents a TEAL value. + */ +export class TealValue extends BaseModel { + /** + * (tb) bytes value. + */ + public bytes: string; + + /** + * (tt) value type. Value `1` refers to **bytes**, value `2` refers to **uint** + */ + public type: number | bigint; + + /** + * (ui) uint value. + */ + public uint: number | bigint; + + /** + * Creates a new `TealValue` object. + * @param bytes - (tb) bytes value. + * @param type - (tt) value type. Value `1` refers to **bytes**, value `2` refers to **uint** + * @param uint - (ui) uint value. + */ + constructor({ + bytes, + type, + uint, + }: { + bytes: string; + type: number | bigint; + uint: number | bigint; + }) { + super(); + this.bytes = bytes; + this.type = type; + this.uint = uint; + + this.attribute_map = { + bytes: 'bytes', + type: 'type', + uint: 'uint', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): TealValue { + /* eslint-disable dot-notation */ + if (typeof data['bytes'] === 'undefined') + throw new Error(`Response is missing required field 'bytes': ${data}`); + if (typeof data['type'] === 'undefined') + throw new Error(`Response is missing required field 'type': ${data}`); + if (typeof data['uint'] === 'undefined') + throw new Error(`Response is missing required field 'uint': ${data}`); + return new TealValue({ + bytes: data['bytes'], + type: data['type'], + uint: data['uint'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Contains all fields common to all transactions and serves as an envelope to all + * transactions type. Represents both regular and inner transactions. + * Definition: + * data/transactions/signedtxn.go : SignedTxn + * data/transactions/transaction.go : Transaction + */ +export class Transaction extends BaseModel { + /** + * (fee) Transaction fee. + */ + public fee: number | bigint; + + /** + * (fv) First valid round for this transaction. + */ + public firstValid: number | bigint; + + /** + * (lv) Last valid round for this transaction. + */ + public lastValid: number | bigint; + + /** + * (snd) Sender's address. + */ + public sender: string; + + /** + * Fields for application transactions. + * Definition: + * data/transactions/application.go : ApplicationCallTxnFields + */ + public applicationTransaction?: TransactionApplication; + + /** + * Fields for asset allocation, re-configuration, and destruction. + * A zero value for asset-id indicates asset creation. + * A zero value for the params indicates asset destruction. + * Definition: + * data/transactions/asset.go : AssetConfigTxnFields + */ + public assetConfigTransaction?: TransactionAssetConfig; + + /** + * Fields for an asset freeze transaction. + * Definition: + * data/transactions/asset.go : AssetFreezeTxnFields + */ + public assetFreezeTransaction?: TransactionAssetFreeze; + + /** + * Fields for an asset transfer transaction. + * Definition: + * data/transactions/asset.go : AssetTransferTxnFields + */ + public assetTransferTransaction?: TransactionAssetTransfer; + + /** + * (sgnr) this is included with signed transactions when the signing address does + * not equal the sender. The backend can use this to ensure that auth addr is equal + * to the accounts auth addr. + */ + public authAddr?: string; + + /** + * (rc) rewards applied to close-remainder-to account. + */ + public closeRewards?: number | bigint; + + /** + * (ca) closing amount for transaction. + */ + public closingAmount?: number | bigint; + + /** + * Round when the transaction was confirmed. + */ + public confirmedRound?: number | bigint; + + /** + * Specifies an application index (ID) if an application was created with this + * transaction. + */ + public createdApplicationIndex?: number | bigint; + + /** + * Specifies an asset index (ID) if an asset was created with this transaction. + */ + public createdAssetIndex?: number | bigint; + + /** + * (gh) Hash of genesis block. + */ + public genesisHash?: Uint8Array; + + /** + * (gen) genesis block ID. + */ + public genesisId?: string; + + /** + * (gd) Global state key/value changes for the application being executed by this + * transaction. + */ + public globalStateDelta?: EvalDeltaKeyValue[]; + + /** + * (grp) Base64 encoded byte array of a sha512/256 digest. When present indicates + * that this transaction is part of a transaction group and the value is the + * sha512/256 hash of the transactions in that group. + */ + public group?: Uint8Array; + + /** + * Transaction ID + */ + public id?: string; + + /** + * Inner transactions produced by application execution. + */ + public innerTxns?: Transaction[]; + + /** + * Offset into the round where this transaction was confirmed. + */ + public intraRoundOffset?: number | bigint; + + /** + * Fields for a keyreg transaction. + * Definition: + * data/transactions/keyreg.go : KeyregTxnFields + */ + public keyregTransaction?: TransactionKeyreg; + + /** + * (lx) Base64 encoded 32-byte array. Lease enforces mutual exclusion of + * transactions. If this field is nonzero, then once the transaction is confirmed, + * it acquires the lease identified by the (Sender, Lease) pair of the transaction + * until the LastValid round passes. While this transaction possesses the lease, no + * other transaction specifying this lease can be confirmed. + */ + public lease?: Uint8Array; + + /** + * (ld) Local state key/value changes for the application being executed by this + * transaction. + */ + public localStateDelta?: AccountStateDelta[]; + + /** + * (lg) Logs for the application being executed by this transaction. + */ + public logs?: Uint8Array[]; + + /** + * (note) Free form data. + */ + public note?: Uint8Array; + + /** + * Fields for a payment transaction. + * Definition: + * data/transactions/payment.go : PaymentTxnFields + */ + public paymentTransaction?: TransactionPayment; + + /** + * (rr) rewards applied to receiver account. + */ + public receiverRewards?: number | bigint; + + /** + * (rekey) when included in a valid transaction, the accounts auth addr will be + * updated with this value and future signatures must be signed with the key + * represented by this address. + */ + public rekeyTo?: string; + + /** + * Time when the block this transaction is in was confirmed. + */ + public roundTime?: number | bigint; + + /** + * (rs) rewards applied to sender account. + */ + public senderRewards?: number | bigint; + + /** + * Validation signature associated with some data. Only one of the signatures + * should be provided. + */ + public signature?: TransactionSignature; + + /** + * Fields for a state proof transaction. + * Definition: + * data/transactions/stateproof.go : StateProofTxnFields + */ + public stateProofTransaction?: TransactionStateProof; + + /** + * (type) Indicates what type of transaction this is. Different types have + * different fields. + * Valid types, and where their fields are stored: + * * (pay) payment-transaction + * * (keyreg) keyreg-transaction + * * (acfg) asset-config-transaction + * * (axfer) asset-transfer-transaction + * * (afrz) asset-freeze-transaction + * * (appl) application-transaction + * * (stpf) state-proof-transaction + */ + public txType?: string; + + /** + * Creates a new `Transaction` object. + * @param fee - (fee) Transaction fee. + * @param firstValid - (fv) First valid round for this transaction. + * @param lastValid - (lv) Last valid round for this transaction. + * @param sender - (snd) Sender's address. + * @param applicationTransaction - Fields for application transactions. + * Definition: + * data/transactions/application.go : ApplicationCallTxnFields + * @param assetConfigTransaction - Fields for asset allocation, re-configuration, and destruction. + * A zero value for asset-id indicates asset creation. + * A zero value for the params indicates asset destruction. + * Definition: + * data/transactions/asset.go : AssetConfigTxnFields + * @param assetFreezeTransaction - Fields for an asset freeze transaction. + * Definition: + * data/transactions/asset.go : AssetFreezeTxnFields + * @param assetTransferTransaction - Fields for an asset transfer transaction. + * Definition: + * data/transactions/asset.go : AssetTransferTxnFields + * @param authAddr - (sgnr) this is included with signed transactions when the signing address does + * not equal the sender. The backend can use this to ensure that auth addr is equal + * to the accounts auth addr. + * @param closeRewards - (rc) rewards applied to close-remainder-to account. + * @param closingAmount - (ca) closing amount for transaction. + * @param confirmedRound - Round when the transaction was confirmed. + * @param createdApplicationIndex - Specifies an application index (ID) if an application was created with this + * transaction. + * @param createdAssetIndex - Specifies an asset index (ID) if an asset was created with this transaction. + * @param genesisHash - (gh) Hash of genesis block. + * @param genesisId - (gen) genesis block ID. + * @param globalStateDelta - (gd) Global state key/value changes for the application being executed by this + * transaction. + * @param group - (grp) Base64 encoded byte array of a sha512/256 digest. When present indicates + * that this transaction is part of a transaction group and the value is the + * sha512/256 hash of the transactions in that group. + * @param id - Transaction ID + * @param innerTxns - Inner transactions produced by application execution. + * @param intraRoundOffset - Offset into the round where this transaction was confirmed. + * @param keyregTransaction - Fields for a keyreg transaction. + * Definition: + * data/transactions/keyreg.go : KeyregTxnFields + * @param lease - (lx) Base64 encoded 32-byte array. Lease enforces mutual exclusion of + * transactions. If this field is nonzero, then once the transaction is confirmed, + * it acquires the lease identified by the (Sender, Lease) pair of the transaction + * until the LastValid round passes. While this transaction possesses the lease, no + * other transaction specifying this lease can be confirmed. + * @param localStateDelta - (ld) Local state key/value changes for the application being executed by this + * transaction. + * @param logs - (lg) Logs for the application being executed by this transaction. + * @param note - (note) Free form data. + * @param paymentTransaction - Fields for a payment transaction. + * Definition: + * data/transactions/payment.go : PaymentTxnFields + * @param receiverRewards - (rr) rewards applied to receiver account. + * @param rekeyTo - (rekey) when included in a valid transaction, the accounts auth addr will be + * updated with this value and future signatures must be signed with the key + * represented by this address. + * @param roundTime - Time when the block this transaction is in was confirmed. + * @param senderRewards - (rs) rewards applied to sender account. + * @param signature - Validation signature associated with some data. Only one of the signatures + * should be provided. + * @param stateProofTransaction - Fields for a state proof transaction. + * Definition: + * data/transactions/stateproof.go : StateProofTxnFields + * @param txType - (type) Indicates what type of transaction this is. Different types have + * different fields. + * Valid types, and where their fields are stored: + * * (pay) payment-transaction + * * (keyreg) keyreg-transaction + * * (acfg) asset-config-transaction + * * (axfer) asset-transfer-transaction + * * (afrz) asset-freeze-transaction + * * (appl) application-transaction + * * (stpf) state-proof-transaction + */ + constructor({ + fee, + firstValid, + lastValid, + sender, + applicationTransaction, + assetConfigTransaction, + assetFreezeTransaction, + assetTransferTransaction, + authAddr, + closeRewards, + closingAmount, + confirmedRound, + createdApplicationIndex, + createdAssetIndex, + genesisHash, + genesisId, + globalStateDelta, + group, + id, + innerTxns, + intraRoundOffset, + keyregTransaction, + lease, + localStateDelta, + logs, + note, + paymentTransaction, + receiverRewards, + rekeyTo, + roundTime, + senderRewards, + signature, + stateProofTransaction, + txType, + }: { + fee: number | bigint; + firstValid: number | bigint; + lastValid: number | bigint; + sender: string; + applicationTransaction?: TransactionApplication; + assetConfigTransaction?: TransactionAssetConfig; + assetFreezeTransaction?: TransactionAssetFreeze; + assetTransferTransaction?: TransactionAssetTransfer; + authAddr?: string; + closeRewards?: number | bigint; + closingAmount?: number | bigint; + confirmedRound?: number | bigint; + createdApplicationIndex?: number | bigint; + createdAssetIndex?: number | bigint; + genesisHash?: string | Uint8Array; + genesisId?: string; + globalStateDelta?: EvalDeltaKeyValue[]; + group?: string | Uint8Array; + id?: string; + innerTxns?: Transaction[]; + intraRoundOffset?: number | bigint; + keyregTransaction?: TransactionKeyreg; + lease?: string | Uint8Array; + localStateDelta?: AccountStateDelta[]; + logs?: Uint8Array[]; + note?: string | Uint8Array; + paymentTransaction?: TransactionPayment; + receiverRewards?: number | bigint; + rekeyTo?: string; + roundTime?: number | bigint; + senderRewards?: number | bigint; + signature?: TransactionSignature; + stateProofTransaction?: TransactionStateProof; + txType?: string; + }) { + super(); + this.fee = fee; + this.firstValid = firstValid; + this.lastValid = lastValid; + this.sender = sender; + this.applicationTransaction = applicationTransaction; + this.assetConfigTransaction = assetConfigTransaction; + this.assetFreezeTransaction = assetFreezeTransaction; + this.assetTransferTransaction = assetTransferTransaction; + this.authAddr = authAddr; + this.closeRewards = closeRewards; + this.closingAmount = closingAmount; + this.confirmedRound = confirmedRound; + this.createdApplicationIndex = createdApplicationIndex; + this.createdAssetIndex = createdAssetIndex; + this.genesisHash = + typeof genesisHash === 'string' + ? new Uint8Array(Buffer.from(genesisHash, 'base64')) + : genesisHash; + this.genesisId = genesisId; + this.globalStateDelta = globalStateDelta; + this.group = + typeof group === 'string' + ? new Uint8Array(Buffer.from(group, 'base64')) + : group; + this.id = id; + this.innerTxns = innerTxns; + this.intraRoundOffset = intraRoundOffset; + this.keyregTransaction = keyregTransaction; + this.lease = + typeof lease === 'string' + ? new Uint8Array(Buffer.from(lease, 'base64')) + : lease; + this.localStateDelta = localStateDelta; + this.logs = logs; + this.note = + typeof note === 'string' + ? new Uint8Array(Buffer.from(note, 'base64')) + : note; + this.paymentTransaction = paymentTransaction; + this.receiverRewards = receiverRewards; + this.rekeyTo = rekeyTo; + this.roundTime = roundTime; + this.senderRewards = senderRewards; + this.signature = signature; + this.stateProofTransaction = stateProofTransaction; + this.txType = txType; + + this.attribute_map = { + fee: 'fee', + firstValid: 'first-valid', + lastValid: 'last-valid', + sender: 'sender', + applicationTransaction: 'application-transaction', + assetConfigTransaction: 'asset-config-transaction', + assetFreezeTransaction: 'asset-freeze-transaction', + assetTransferTransaction: 'asset-transfer-transaction', + authAddr: 'auth-addr', + closeRewards: 'close-rewards', + closingAmount: 'closing-amount', + confirmedRound: 'confirmed-round', + createdApplicationIndex: 'created-application-index', + createdAssetIndex: 'created-asset-index', + genesisHash: 'genesis-hash', + genesisId: 'genesis-id', + globalStateDelta: 'global-state-delta', + group: 'group', + id: 'id', + innerTxns: 'inner-txns', + intraRoundOffset: 'intra-round-offset', + keyregTransaction: 'keyreg-transaction', + lease: 'lease', + localStateDelta: 'local-state-delta', + logs: 'logs', + note: 'note', + paymentTransaction: 'payment-transaction', + receiverRewards: 'receiver-rewards', + rekeyTo: 'rekey-to', + roundTime: 'round-time', + senderRewards: 'sender-rewards', + signature: 'signature', + stateProofTransaction: 'state-proof-transaction', + txType: 'tx-type', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): Transaction { + /* eslint-disable dot-notation */ + if (typeof data['fee'] === 'undefined') + throw new Error(`Response is missing required field 'fee': ${data}`); + if (typeof data['first-valid'] === 'undefined') + throw new Error( + `Response is missing required field 'first-valid': ${data}` + ); + if (typeof data['last-valid'] === 'undefined') + throw new Error( + `Response is missing required field 'last-valid': ${data}` + ); + if (typeof data['sender'] === 'undefined') + throw new Error(`Response is missing required field 'sender': ${data}`); + return new Transaction({ + fee: data['fee'], + firstValid: data['first-valid'], + lastValid: data['last-valid'], + sender: data['sender'], + applicationTransaction: + typeof data['application-transaction'] !== 'undefined' + ? TransactionApplication.from_obj_for_encoding( + data['application-transaction'] + ) + : undefined, + assetConfigTransaction: + typeof data['asset-config-transaction'] !== 'undefined' + ? TransactionAssetConfig.from_obj_for_encoding( + data['asset-config-transaction'] + ) + : undefined, + assetFreezeTransaction: + typeof data['asset-freeze-transaction'] !== 'undefined' + ? TransactionAssetFreeze.from_obj_for_encoding( + data['asset-freeze-transaction'] + ) + : undefined, + assetTransferTransaction: + typeof data['asset-transfer-transaction'] !== 'undefined' + ? TransactionAssetTransfer.from_obj_for_encoding( + data['asset-transfer-transaction'] + ) + : undefined, + authAddr: data['auth-addr'], + closeRewards: data['close-rewards'], + closingAmount: data['closing-amount'], + confirmedRound: data['confirmed-round'], + createdApplicationIndex: data['created-application-index'], + createdAssetIndex: data['created-asset-index'], + genesisHash: data['genesis-hash'], + genesisId: data['genesis-id'], + globalStateDelta: + typeof data['global-state-delta'] !== 'undefined' + ? data['global-state-delta'].map( + EvalDeltaKeyValue.from_obj_for_encoding + ) + : undefined, + group: data['group'], + id: data['id'], + innerTxns: + typeof data['inner-txns'] !== 'undefined' + ? data['inner-txns'].map(Transaction.from_obj_for_encoding) + : undefined, + intraRoundOffset: data['intra-round-offset'], + keyregTransaction: + typeof data['keyreg-transaction'] !== 'undefined' + ? TransactionKeyreg.from_obj_for_encoding(data['keyreg-transaction']) + : undefined, + lease: data['lease'], + localStateDelta: + typeof data['local-state-delta'] !== 'undefined' + ? data['local-state-delta'].map( + AccountStateDelta.from_obj_for_encoding + ) + : undefined, + logs: data['logs'], + note: data['note'], + paymentTransaction: + typeof data['payment-transaction'] !== 'undefined' + ? TransactionPayment.from_obj_for_encoding( + data['payment-transaction'] + ) + : undefined, + receiverRewards: data['receiver-rewards'], + rekeyTo: data['rekey-to'], + roundTime: data['round-time'], + senderRewards: data['sender-rewards'], + signature: + typeof data['signature'] !== 'undefined' + ? TransactionSignature.from_obj_for_encoding(data['signature']) + : undefined, + stateProofTransaction: + typeof data['state-proof-transaction'] !== 'undefined' + ? TransactionStateProof.from_obj_for_encoding( + data['state-proof-transaction'] + ) + : undefined, + txType: data['tx-type'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Fields for application transactions. + * Definition: + * data/transactions/application.go : ApplicationCallTxnFields + */ +export class TransactionApplication extends BaseModel { + /** + * (apid) ID of the application being configured or empty if creating. + */ + public applicationId: number | bigint; + + /** + * (apat) List of accounts in addition to the sender that may be accessed from the + * application's approval-program and clear-state-program. + */ + public accounts?: string[]; + + /** + * (apaa) transaction specific arguments accessed from the application's + * approval-program and clear-state-program. + */ + public applicationArgs?: Uint8Array[]; + + /** + * (apap) Logic executed for every application transaction, except when + * on-completion is set to "clear". It can read and write global state for the + * application, as well as account-specific local state. Approval programs may + * reject the transaction. + */ + public approvalProgram?: Uint8Array; + + /** + * (apsu) Logic executed for application transactions with on-completion set to + * "clear". It can read and write global state for the application, as well as + * account-specific local state. Clear state programs cannot reject the + * transaction. + */ + public clearStateProgram?: Uint8Array; + + /** + * (epp) specifies the additional app program len requested in pages. + */ + public extraProgramPages?: number | bigint; + + /** + * (apfa) Lists the applications in addition to the application-id whose global + * states may be accessed by this application's approval-program and + * clear-state-program. The access is read-only. + */ + public foreignApps?: (number | bigint)[]; + + /** + * (apas) lists the assets whose parameters may be accessed by this application's + * ApprovalProgram and ClearStateProgram. The access is read-only. + */ + public foreignAssets?: (number | bigint)[]; + + /** + * Represents a (apls) local-state or (apgs) global-state schema. These schemas + * determine how much storage may be used in a local-state or global-state for an + * application. The more space used, the larger minimum balance must be maintained + * in the account holding the data. + */ + public globalStateSchema?: StateSchema; + + /** + * Represents a (apls) local-state or (apgs) global-state schema. These schemas + * determine how much storage may be used in a local-state or global-state for an + * application. The more space used, the larger minimum balance must be maintained + * in the account holding the data. + */ + public localStateSchema?: StateSchema; + + /** + * (apan) defines the what additional actions occur with the transaction. + * Valid types: + * * noop + * * optin + * * closeout + * * clear + * * update + * * update + * * delete + */ + public onCompletion?: string; + + /** + * Creates a new `TransactionApplication` object. + * @param applicationId - (apid) ID of the application being configured or empty if creating. + * @param accounts - (apat) List of accounts in addition to the sender that may be accessed from the + * application's approval-program and clear-state-program. + * @param applicationArgs - (apaa) transaction specific arguments accessed from the application's + * approval-program and clear-state-program. + * @param approvalProgram - (apap) Logic executed for every application transaction, except when + * on-completion is set to "clear". It can read and write global state for the + * application, as well as account-specific local state. Approval programs may + * reject the transaction. + * @param clearStateProgram - (apsu) Logic executed for application transactions with on-completion set to + * "clear". It can read and write global state for the application, as well as + * account-specific local state. Clear state programs cannot reject the + * transaction. + * @param extraProgramPages - (epp) specifies the additional app program len requested in pages. + * @param foreignApps - (apfa) Lists the applications in addition to the application-id whose global + * states may be accessed by this application's approval-program and + * clear-state-program. The access is read-only. + * @param foreignAssets - (apas) lists the assets whose parameters may be accessed by this application's + * ApprovalProgram and ClearStateProgram. The access is read-only. + * @param globalStateSchema - Represents a (apls) local-state or (apgs) global-state schema. These schemas + * determine how much storage may be used in a local-state or global-state for an + * application. The more space used, the larger minimum balance must be maintained + * in the account holding the data. + * @param localStateSchema - Represents a (apls) local-state or (apgs) global-state schema. These schemas + * determine how much storage may be used in a local-state or global-state for an + * application. The more space used, the larger minimum balance must be maintained + * in the account holding the data. + * @param onCompletion - (apan) defines the what additional actions occur with the transaction. + * Valid types: + * * noop + * * optin + * * closeout + * * clear + * * update + * * update + * * delete + */ + constructor({ + applicationId, + accounts, + applicationArgs, + approvalProgram, + clearStateProgram, + extraProgramPages, + foreignApps, + foreignAssets, + globalStateSchema, + localStateSchema, + onCompletion, + }: { + applicationId: number | bigint; + accounts?: string[]; + applicationArgs?: Uint8Array[]; + approvalProgram?: string | Uint8Array; + clearStateProgram?: string | Uint8Array; + extraProgramPages?: number | bigint; + foreignApps?: (number | bigint)[]; + foreignAssets?: (number | bigint)[]; + globalStateSchema?: StateSchema; + localStateSchema?: StateSchema; + onCompletion?: string; + }) { + super(); + this.applicationId = applicationId; + this.accounts = accounts; + this.applicationArgs = applicationArgs; + this.approvalProgram = + typeof approvalProgram === 'string' + ? new Uint8Array(Buffer.from(approvalProgram, 'base64')) + : approvalProgram; + this.clearStateProgram = + typeof clearStateProgram === 'string' + ? new Uint8Array(Buffer.from(clearStateProgram, 'base64')) + : clearStateProgram; + this.extraProgramPages = extraProgramPages; + this.foreignApps = foreignApps; + this.foreignAssets = foreignAssets; + this.globalStateSchema = globalStateSchema; + this.localStateSchema = localStateSchema; + this.onCompletion = onCompletion; + + this.attribute_map = { + applicationId: 'application-id', + accounts: 'accounts', + applicationArgs: 'application-args', + approvalProgram: 'approval-program', + clearStateProgram: 'clear-state-program', + extraProgramPages: 'extra-program-pages', + foreignApps: 'foreign-apps', + foreignAssets: 'foreign-assets', + globalStateSchema: 'global-state-schema', + localStateSchema: 'local-state-schema', + onCompletion: 'on-completion', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionApplication { + /* eslint-disable dot-notation */ + if (typeof data['application-id'] === 'undefined') + throw new Error( + `Response is missing required field 'application-id': ${data}` + ); + return new TransactionApplication({ + applicationId: data['application-id'], + accounts: data['accounts'], + applicationArgs: data['application-args'], + approvalProgram: data['approval-program'], + clearStateProgram: data['clear-state-program'], + extraProgramPages: data['extra-program-pages'], + foreignApps: data['foreign-apps'], + foreignAssets: data['foreign-assets'], + globalStateSchema: + typeof data['global-state-schema'] !== 'undefined' + ? StateSchema.from_obj_for_encoding(data['global-state-schema']) + : undefined, + localStateSchema: + typeof data['local-state-schema'] !== 'undefined' + ? StateSchema.from_obj_for_encoding(data['local-state-schema']) + : undefined, + onCompletion: data['on-completion'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Fields for asset allocation, re-configuration, and destruction. + * A zero value for asset-id indicates asset creation. + * A zero value for the params indicates asset destruction. + * Definition: + * data/transactions/asset.go : AssetConfigTxnFields + */ +export class TransactionAssetConfig extends BaseModel { + /** + * (xaid) ID of the asset being configured or empty if creating. + */ + public assetId?: number | bigint; + + /** + * AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + */ + public params?: AssetParams; + + /** + * Creates a new `TransactionAssetConfig` object. + * @param assetId - (xaid) ID of the asset being configured or empty if creating. + * @param params - AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + */ + constructor({ + assetId, + params, + }: { + assetId?: number | bigint; + params?: AssetParams; + }) { + super(); + this.assetId = assetId; + this.params = params; + + this.attribute_map = { + assetId: 'asset-id', + params: 'params', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionAssetConfig { + /* eslint-disable dot-notation */ + return new TransactionAssetConfig({ + assetId: data['asset-id'], + params: + typeof data['params'] !== 'undefined' + ? AssetParams.from_obj_for_encoding(data['params']) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Fields for an asset freeze transaction. + * Definition: + * data/transactions/asset.go : AssetFreezeTxnFields + */ +export class TransactionAssetFreeze extends BaseModel { + /** + * (fadd) Address of the account whose asset is being frozen or thawed. + */ + public address: string; + + /** + * (faid) ID of the asset being frozen or thawed. + */ + public assetId: number | bigint; + + /** + * (afrz) The new freeze status. + */ + public newFreezeStatus: boolean; + + /** + * Creates a new `TransactionAssetFreeze` object. + * @param address - (fadd) Address of the account whose asset is being frozen or thawed. + * @param assetId - (faid) ID of the asset being frozen or thawed. + * @param newFreezeStatus - (afrz) The new freeze status. + */ + constructor({ + address, + assetId, + newFreezeStatus, + }: { + address: string; + assetId: number | bigint; + newFreezeStatus: boolean; + }) { + super(); + this.address = address; + this.assetId = assetId; + this.newFreezeStatus = newFreezeStatus; + + this.attribute_map = { + address: 'address', + assetId: 'asset-id', + newFreezeStatus: 'new-freeze-status', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionAssetFreeze { + /* eslint-disable dot-notation */ + if (typeof data['address'] === 'undefined') + throw new Error(`Response is missing required field 'address': ${data}`); + if (typeof data['asset-id'] === 'undefined') + throw new Error(`Response is missing required field 'asset-id': ${data}`); + if (typeof data['new-freeze-status'] === 'undefined') + throw new Error( + `Response is missing required field 'new-freeze-status': ${data}` + ); + return new TransactionAssetFreeze({ + address: data['address'], + assetId: data['asset-id'], + newFreezeStatus: data['new-freeze-status'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Fields for an asset transfer transaction. + * Definition: + * data/transactions/asset.go : AssetTransferTxnFields + */ +export class TransactionAssetTransfer extends BaseModel { + /** + * (aamt) Amount of asset to transfer. A zero amount transferred to self allocates + * that asset in the account's Assets map. + */ + public amount: number | bigint; + + /** + * (xaid) ID of the asset being transferred. + */ + public assetId: number | bigint; + + /** + * (arcv) Recipient address of the transfer. + */ + public receiver: string; + + /** + * Number of assets transfered to the close-to account as part of the transaction. + */ + public closeAmount?: number | bigint; + + /** + * (aclose) Indicates that the asset should be removed from the account's Assets + * map, and specifies where the remaining asset holdings should be transferred. + * It's always valid to transfer remaining asset holdings to the creator account. + */ + public closeTo?: string; + + /** + * (asnd) The effective sender during a clawback transactions. If this is not a + * zero value, the real transaction sender must be the Clawback address from the + * AssetParams. + */ + public sender?: string; + + /** + * Creates a new `TransactionAssetTransfer` object. + * @param amount - (aamt) Amount of asset to transfer. A zero amount transferred to self allocates + * that asset in the account's Assets map. + * @param assetId - (xaid) ID of the asset being transferred. + * @param receiver - (arcv) Recipient address of the transfer. + * @param closeAmount - Number of assets transfered to the close-to account as part of the transaction. + * @param closeTo - (aclose) Indicates that the asset should be removed from the account's Assets + * map, and specifies where the remaining asset holdings should be transferred. + * It's always valid to transfer remaining asset holdings to the creator account. + * @param sender - (asnd) The effective sender during a clawback transactions. If this is not a + * zero value, the real transaction sender must be the Clawback address from the + * AssetParams. + */ + constructor({ + amount, + assetId, + receiver, + closeAmount, + closeTo, + sender, + }: { + amount: number | bigint; + assetId: number | bigint; + receiver: string; + closeAmount?: number | bigint; + closeTo?: string; + sender?: string; + }) { + super(); + this.amount = amount; + this.assetId = assetId; + this.receiver = receiver; + this.closeAmount = closeAmount; + this.closeTo = closeTo; + this.sender = sender; + + this.attribute_map = { + amount: 'amount', + assetId: 'asset-id', + receiver: 'receiver', + closeAmount: 'close-amount', + closeTo: 'close-to', + sender: 'sender', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionAssetTransfer { + /* eslint-disable dot-notation */ + if (typeof data['amount'] === 'undefined') + throw new Error(`Response is missing required field 'amount': ${data}`); + if (typeof data['asset-id'] === 'undefined') + throw new Error(`Response is missing required field 'asset-id': ${data}`); + if (typeof data['receiver'] === 'undefined') + throw new Error(`Response is missing required field 'receiver': ${data}`); + return new TransactionAssetTransfer({ + amount: data['amount'], + assetId: data['asset-id'], + receiver: data['receiver'], + closeAmount: data['close-amount'], + closeTo: data['close-to'], + sender: data['sender'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Fields for a keyreg transaction. + * Definition: + * data/transactions/keyreg.go : KeyregTxnFields + */ +export class TransactionKeyreg extends BaseModel { + /** + * (nonpart) Mark the account as participating or non-participating. + */ + public nonParticipation?: boolean; + + /** + * (selkey) Public key used with the Verified Random Function (VRF) result during + * committee selection. + */ + public selectionParticipationKey?: Uint8Array; + + /** + * (sprfkey) State proof key used in key registration transactions. + */ + public stateProofKey?: Uint8Array; + + /** + * (votefst) First round this participation key is valid. + */ + public voteFirstValid?: number | bigint; + + /** + * (votekd) Number of subkeys in each batch of participation keys. + */ + public voteKeyDilution?: number | bigint; + + /** + * (votelst) Last round this participation key is valid. + */ + public voteLastValid?: number | bigint; + + /** + * (votekey) Participation public key used in key registration transactions. + */ + public voteParticipationKey?: Uint8Array; + + /** + * Creates a new `TransactionKeyreg` object. + * @param nonParticipation - (nonpart) Mark the account as participating or non-participating. + * @param selectionParticipationKey - (selkey) Public key used with the Verified Random Function (VRF) result during + * committee selection. + * @param stateProofKey - (sprfkey) State proof key used in key registration transactions. + * @param voteFirstValid - (votefst) First round this participation key is valid. + * @param voteKeyDilution - (votekd) Number of subkeys in each batch of participation keys. + * @param voteLastValid - (votelst) Last round this participation key is valid. + * @param voteParticipationKey - (votekey) Participation public key used in key registration transactions. + */ + constructor({ + nonParticipation, + selectionParticipationKey, + stateProofKey, + voteFirstValid, + voteKeyDilution, + voteLastValid, + voteParticipationKey, + }: { + nonParticipation?: boolean; + selectionParticipationKey?: string | Uint8Array; + stateProofKey?: string | Uint8Array; + voteFirstValid?: number | bigint; + voteKeyDilution?: number | bigint; + voteLastValid?: number | bigint; + voteParticipationKey?: string | Uint8Array; + }) { + super(); + this.nonParticipation = nonParticipation; + this.selectionParticipationKey = + typeof selectionParticipationKey === 'string' + ? new Uint8Array(Buffer.from(selectionParticipationKey, 'base64')) + : selectionParticipationKey; + this.stateProofKey = + typeof stateProofKey === 'string' + ? new Uint8Array(Buffer.from(stateProofKey, 'base64')) + : stateProofKey; + this.voteFirstValid = voteFirstValid; + this.voteKeyDilution = voteKeyDilution; + this.voteLastValid = voteLastValid; + this.voteParticipationKey = + typeof voteParticipationKey === 'string' + ? new Uint8Array(Buffer.from(voteParticipationKey, 'base64')) + : voteParticipationKey; + + this.attribute_map = { + nonParticipation: 'non-participation', + selectionParticipationKey: 'selection-participation-key', + stateProofKey: 'state-proof-key', + voteFirstValid: 'vote-first-valid', + voteKeyDilution: 'vote-key-dilution', + voteLastValid: 'vote-last-valid', + voteParticipationKey: 'vote-participation-key', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): TransactionKeyreg { + /* eslint-disable dot-notation */ + return new TransactionKeyreg({ + nonParticipation: data['non-participation'], + selectionParticipationKey: data['selection-participation-key'], + stateProofKey: data['state-proof-key'], + voteFirstValid: data['vote-first-valid'], + voteKeyDilution: data['vote-key-dilution'], + voteLastValid: data['vote-last-valid'], + voteParticipationKey: data['vote-participation-key'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Fields for a payment transaction. + * Definition: + * data/transactions/payment.go : PaymentTxnFields + */ +export class TransactionPayment extends BaseModel { + /** + * (amt) number of MicroAlgos intended to be transferred. + */ + public amount: number | bigint; + + /** + * (rcv) receiver's address. + */ + public receiver: string; + + /** + * Number of MicroAlgos that were sent to the close-remainder-to address when + * closing the sender account. + */ + public closeAmount?: number | bigint; + + /** + * (close) when set, indicates that the sending account should be closed and all + * remaining funds be transferred to this address. + */ + public closeRemainderTo?: string; + + /** + * Creates a new `TransactionPayment` object. + * @param amount - (amt) number of MicroAlgos intended to be transferred. + * @param receiver - (rcv) receiver's address. + * @param closeAmount - Number of MicroAlgos that were sent to the close-remainder-to address when + * closing the sender account. + * @param closeRemainderTo - (close) when set, indicates that the sending account should be closed and all + * remaining funds be transferred to this address. + */ + constructor({ + amount, + receiver, + closeAmount, + closeRemainderTo, + }: { + amount: number | bigint; + receiver: string; + closeAmount?: number | bigint; + closeRemainderTo?: string; + }) { + super(); + this.amount = amount; + this.receiver = receiver; + this.closeAmount = closeAmount; + this.closeRemainderTo = closeRemainderTo; + + this.attribute_map = { + amount: 'amount', + receiver: 'receiver', + closeAmount: 'close-amount', + closeRemainderTo: 'close-remainder-to', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): TransactionPayment { + /* eslint-disable dot-notation */ + if (typeof data['amount'] === 'undefined') + throw new Error(`Response is missing required field 'amount': ${data}`); + if (typeof data['receiver'] === 'undefined') + throw new Error(`Response is missing required field 'receiver': ${data}`); + return new TransactionPayment({ + amount: data['amount'], + receiver: data['receiver'], + closeAmount: data['close-amount'], + closeRemainderTo: data['close-remainder-to'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class TransactionResponse extends BaseModel { + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + /** + * Contains all fields common to all transactions and serves as an envelope to all + * transactions type. Represents both regular and inner transactions. + * Definition: + * data/transactions/signedtxn.go : SignedTxn + * data/transactions/transaction.go : Transaction + */ + public transaction: Transaction; + + /** + * Creates a new `TransactionResponse` object. + * @param currentRound - Round at which the results were computed. + * @param transaction - Contains all fields common to all transactions and serves as an envelope to all + * transactions type. Represents both regular and inner transactions. + * Definition: + * data/transactions/signedtxn.go : SignedTxn + * data/transactions/transaction.go : Transaction + */ + constructor({ + currentRound, + transaction, + }: { + currentRound: number | bigint; + transaction: Transaction; + }) { + super(); + this.currentRound = currentRound; + this.transaction = transaction; + + this.attribute_map = { + currentRound: 'current-round', + transaction: 'transaction', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): TransactionResponse { + /* eslint-disable dot-notation */ + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + if (typeof data['transaction'] === 'undefined') + throw new Error( + `Response is missing required field 'transaction': ${data}` + ); + return new TransactionResponse({ + currentRound: data['current-round'], + transaction: Transaction.from_obj_for_encoding(data['transaction']), + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Validation signature associated with some data. Only one of the signatures + * should be provided. + */ +export class TransactionSignature extends BaseModel { + /** + * (lsig) Programatic transaction signature. + * Definition: + * data/transactions/logicsig.go + */ + public logicsig?: TransactionSignatureLogicsig; + + /** + * (msig) structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + */ + public multisig?: TransactionSignatureMultisig; + + /** + * (sig) Standard ed25519 signature. + */ + public sig?: Uint8Array; + + /** + * Creates a new `TransactionSignature` object. + * @param logicsig - (lsig) Programatic transaction signature. + * Definition: + * data/transactions/logicsig.go + * @param multisig - (msig) structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + * @param sig - (sig) Standard ed25519 signature. + */ + constructor({ + logicsig, + multisig, + sig, + }: { + logicsig?: TransactionSignatureLogicsig; + multisig?: TransactionSignatureMultisig; + sig?: string | Uint8Array; + }) { + super(); + this.logicsig = logicsig; + this.multisig = multisig; + this.sig = + typeof sig === 'string' + ? new Uint8Array(Buffer.from(sig, 'base64')) + : sig; + + this.attribute_map = { + logicsig: 'logicsig', + multisig: 'multisig', + sig: 'sig', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionSignature { + /* eslint-disable dot-notation */ + return new TransactionSignature({ + logicsig: + typeof data['logicsig'] !== 'undefined' + ? TransactionSignatureLogicsig.from_obj_for_encoding(data['logicsig']) + : undefined, + multisig: + typeof data['multisig'] !== 'undefined' + ? TransactionSignatureMultisig.from_obj_for_encoding(data['multisig']) + : undefined, + sig: data['sig'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * (lsig) Programatic transaction signature. + * Definition: + * data/transactions/logicsig.go + */ +export class TransactionSignatureLogicsig extends BaseModel { + /** + * (l) Program signed by a signature or multi signature, or hashed to be the + * address of ana ccount. Base64 encoded TEAL program. + */ + public logic: Uint8Array; + + /** + * (arg) Logic arguments, base64 encoded. + */ + public args?: Uint8Array[]; + + /** + * (msig) structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + */ + public multisigSignature?: TransactionSignatureMultisig; + + /** + * (sig) ed25519 signature. + */ + public signature?: Uint8Array; + + /** + * Creates a new `TransactionSignatureLogicsig` object. + * @param logic - (l) Program signed by a signature or multi signature, or hashed to be the + * address of ana ccount. Base64 encoded TEAL program. + * @param args - (arg) Logic arguments, base64 encoded. + * @param multisigSignature - (msig) structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + * @param signature - (sig) ed25519 signature. + */ + constructor({ + logic, + args, + multisigSignature, + signature, + }: { + logic: string | Uint8Array; + args?: Uint8Array[]; + multisigSignature?: TransactionSignatureMultisig; + signature?: string | Uint8Array; + }) { + super(); + this.logic = + typeof logic === 'string' + ? new Uint8Array(Buffer.from(logic, 'base64')) + : logic; + this.args = args; + this.multisigSignature = multisigSignature; + this.signature = + typeof signature === 'string' + ? new Uint8Array(Buffer.from(signature, 'base64')) + : signature; + + this.attribute_map = { + logic: 'logic', + args: 'args', + multisigSignature: 'multisig-signature', + signature: 'signature', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionSignatureLogicsig { + /* eslint-disable dot-notation */ + if (typeof data['logic'] === 'undefined') + throw new Error(`Response is missing required field 'logic': ${data}`); + return new TransactionSignatureLogicsig({ + logic: data['logic'], + args: data['args'], + multisigSignature: + typeof data['multisig-signature'] !== 'undefined' + ? TransactionSignatureMultisig.from_obj_for_encoding( + data['multisig-signature'] + ) + : undefined, + signature: data['signature'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * (msig) structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + */ +export class TransactionSignatureMultisig extends BaseModel { + /** + * (subsig) holds pairs of public key and signatures. + */ + public subsignature?: TransactionSignatureMultisigSubsignature[]; + + /** + * (thr) + */ + public threshold?: number | bigint; + + /** + * (v) + */ + public version?: number | bigint; + + /** + * Creates a new `TransactionSignatureMultisig` object. + * @param subsignature - (subsig) holds pairs of public key and signatures. + * @param threshold - (thr) + * @param version - (v) + */ + constructor({ + subsignature, + threshold, + version, + }: { + subsignature?: TransactionSignatureMultisigSubsignature[]; + threshold?: number | bigint; + version?: number | bigint; + }) { + super(); + this.subsignature = subsignature; + this.threshold = threshold; + this.version = version; + + this.attribute_map = { + subsignature: 'subsignature', + threshold: 'threshold', + version: 'version', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionSignatureMultisig { + /* eslint-disable dot-notation */ + return new TransactionSignatureMultisig({ + subsignature: + typeof data['subsignature'] !== 'undefined' + ? data['subsignature'].map( + TransactionSignatureMultisigSubsignature.from_obj_for_encoding + ) + : undefined, + threshold: data['threshold'], + version: data['version'], + }); + /* eslint-enable dot-notation */ + } +} + +export class TransactionSignatureMultisigSubsignature extends BaseModel { + /** + * (pk) + */ + public publicKey?: Uint8Array; + + /** + * (s) + */ + public signature?: Uint8Array; + + /** + * Creates a new `TransactionSignatureMultisigSubsignature` object. + * @param publicKey - (pk) + * @param signature - (s) + */ + constructor({ + publicKey, + signature, + }: { + publicKey?: string | Uint8Array; + signature?: string | Uint8Array; + }) { + super(); + this.publicKey = + typeof publicKey === 'string' + ? new Uint8Array(Buffer.from(publicKey, 'base64')) + : publicKey; + this.signature = + typeof signature === 'string' + ? new Uint8Array(Buffer.from(signature, 'base64')) + : signature; + + this.attribute_map = { + publicKey: 'public-key', + signature: 'signature', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionSignatureMultisigSubsignature { + /* eslint-disable dot-notation */ + return new TransactionSignatureMultisigSubsignature({ + publicKey: data['public-key'], + signature: data['signature'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * Fields for a state proof transaction. + * Definition: + * data/transactions/stateproof.go : StateProofTxnFields + */ +export class TransactionStateProof extends BaseModel { + /** + * (spmsg) + */ + public message?: IndexerStateProofMessage; + + /** + * (sp) represents a state proof. + * Definition: + * crypto/stateproof/structs.go : StateProof + */ + public stateProof?: StateProofFields; + + /** + * (sptype) Type of the state proof. Integer representing an entry defined in + * protocol/stateproof.go + */ + public stateProofType?: number | bigint; + + /** + * Creates a new `TransactionStateProof` object. + * @param message - (spmsg) + * @param stateProof - (sp) represents a state proof. + * Definition: + * crypto/stateproof/structs.go : StateProof + * @param stateProofType - (sptype) Type of the state proof. Integer representing an entry defined in + * protocol/stateproof.go + */ + constructor({ + message, + stateProof, + stateProofType, + }: { + message?: IndexerStateProofMessage; + stateProof?: StateProofFields; + stateProofType?: number | bigint; + }) { + super(); + this.message = message; + this.stateProof = stateProof; + this.stateProofType = stateProofType; + + this.attribute_map = { + message: 'message', + stateProof: 'state-proof', + stateProofType: 'state-proof-type', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionStateProof { + /* eslint-disable dot-notation */ + return new TransactionStateProof({ + message: + typeof data['message'] !== 'undefined' + ? IndexerStateProofMessage.from_obj_for_encoding(data['message']) + : undefined, + stateProof: + typeof data['state-proof'] !== 'undefined' + ? StateProofFields.from_obj_for_encoding(data['state-proof']) + : undefined, + stateProofType: data['state-proof-type'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * + */ +export class TransactionsResponse extends BaseModel { + /** + * Round at which the results were computed. + */ + public currentRound: number | bigint; + + public transactions: Transaction[]; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `TransactionsResponse` object. + * @param currentRound - Round at which the results were computed. + * @param transactions - + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + currentRound, + transactions, + nextToken, + }: { + currentRound: number | bigint; + transactions: Transaction[]; + nextToken?: string; + }) { + super(); + this.currentRound = currentRound; + this.transactions = transactions; + this.nextToken = nextToken; + + this.attribute_map = { + currentRound: 'current-round', + transactions: 'transactions', + nextToken: 'next-token', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): TransactionsResponse { + /* eslint-disable dot-notation */ + if (typeof data['current-round'] === 'undefined') + throw new Error( + `Response is missing required field 'current-round': ${data}` + ); + if (!Array.isArray(data['transactions'])) + throw new Error( + `Response is missing required array field 'transactions': ${data}` + ); + return new TransactionsResponse({ + currentRound: data['current-round'], + transactions: data['transactions'].map(Transaction.from_obj_for_encoding), + nextToken: data['next-token'], + }); + /* eslint-enable dot-notation */ + } +} diff --git a/src/client/v2/indexer/searchAccounts.ts b/src/client/v2/indexer/searchAccounts.ts new file mode 100644 index 0000000..21b1df4 --- /dev/null +++ b/src/client/v2/indexer/searchAccounts.ts @@ -0,0 +1,268 @@ +import JSONRequest from '../jsonrequest'; + +/** + * Returns information about indexed accounts. + * + * #### Example + * ```typescript + * const accounts = await indexerClient.searchAccounts().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accounts) + * @category GET + */ +export default class SearchAccounts extends JSONRequest { + /** + * @returns `/v2/accounts` + */ + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/accounts'; + } + + /** + * Filtered results should have an amount greater than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const minBalance = 300000; + * const accounts = await indexerClient + * .searchAccounts() + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const minBalance = 300000; + * const accounts = await indexerClient + * .searchAccounts() + * .assetID(assetID) + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * @remarks + * If you are looking for accounts with the currency amount greater than 0, simply construct the query without `currencyGreaterThan` because it doesn't accept `-1`, and passing the `0` `currency-greater-than` value would exclude accounts with a 0 amount. + * + * @param greater + * @category query + */ + currencyGreaterThan(greater: number) { + this.query['currency-greater-than'] = greater; + return this; + } + + /** + * Filtered results should have an amount less than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const maxBalance = 500000; + * const accounts = await indexerClient + * .searchAccounts() + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const maxBalance = 500000; + * const accounts = await indexerClient + * .searchAccounts() + * .assetID(assetID) + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * @param lesser + * @category query + */ + currencyLessThan(lesser: number) { + this.query['currency-less-than'] = lesser; + return this; + } + + /** + * Maximum number of results to return. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const accounts = await indexerClient + * .searchAccounts() + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Asset ID to filter with. + * + * #### Example + * ```typescript + * const assetID = 163650; + * const accounts = await indexerClient + * .searchAccounts() + * .assetID(assetID) + * .do(); + * ``` + * + * @param id + * @category query + */ + assetID(id: number) { + this.query['asset-id'] = id; + return this; + } + + /** + * The next page of results. + * + * #### Example + * ```typescript + * const maxResults = 25; + * + * const accountsPage1 = await indexerClient + * .searchAccounts() + * .limit(maxResults) + * .do(); + * + * const accountsPage2 = await indexerClient + * .searchAccounts() + * .limit(maxResults) + * .nextToken(accountsPage1["next-token"]) + * .do(); + * ``` + * + * @param nextToken - provided by the previous results + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include results for the specified round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const accounts = await indexerClient + * .searchAccounts() + * .round(targetBlock) + * .do(); + * ``` + * @remarks For performance reasons, this parameter may be disabled on some configurations. + * @param round + * @category query + */ + round(round: number) { + this.query.round = round; + return this; + } + + /** + * Include accounts that use this spending key. + * + * #### Example + * ```typescript + * const authAddr = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accounts = await indexerClient + * .searchAccounts() + * .authAddr(authAddr) + * .do(); + * ``` + * + * @param authAddr + */ + authAddr(authAddr: string) { + this.query['auth-addr'] = authAddr; + return this; + } + + /** + * Filter for this application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const accounts = await indexerClient + * .searchAccounts() + * .applicationID(appId) + * .do(); + * ``` + * + * @param applicationID + * @category query + */ + applicationID(applicationID: number) { + this.query['application-id'] = applicationID; + return this; + } + + /** + * Includes all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example 1 + * ```typescript + * const assetId = 163650; + * const accounts = await indexerClient + * .searchAccounts() + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetId = 163650; + * const accounts = await indexerClient + * .searchAccounts() + * .includeAll() + * .do(); + * ``` + * + * @param value - default true when called without passing a value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Exclude additional items such as asset holdings, application local data stored for this account, asset parameters created by this account, and application parameters created by this account. + * + * #### Example 1 + * ```typescript + * const accounts = await indexerClient + * .searchAccounts() + * .exclude("all") + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const accounts = await indexerClient + * .searchAccounts() + * .exclude("assets,created-assets") + * .do(); + * ``` + * @remarks By default, it behaves as exclude=none + * @param exclude - Array of `all`, `assets`, `created-assets`, `apps-local-state`, `created-apps`, `none` + * @category query + */ + exclude(exclude: string) { + this.query.exclude = exclude; + return this; + } +} diff --git a/src/client/v2/indexer/searchForApplicationBoxes.ts b/src/client/v2/indexer/searchForApplicationBoxes.ts new file mode 100644 index 0000000..b881de4 --- /dev/null +++ b/src/client/v2/indexer/searchForApplicationBoxes.ts @@ -0,0 +1,101 @@ +import JSONRequest from '../jsonrequest'; +import HTTPClient from '../../client'; +import IntDecoding from '../../../types/intDecoding'; +import { BoxesResponse } from './models/types'; + +export default class SearchForApplicationBoxes extends JSONRequest< + BoxesResponse, + Record +> { + /** + * Returns information about indexed application boxes. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const appID = 1234; + * + * const responsePage1 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .do(); + * const boxNamesPage1 = responsePage1.boxes.map(box => box.name); + * + * const responsePage2 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .nextToken(responsePage1.nextToken) + * .do(); + * const boxNamesPage2 = responsePage2.boxes.map(box => box.name); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idboxes) + * @oaram index - application index. + * @category GET + */ + constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { + super(c, intDecoding); + this.index = index; + } + + /** + * @returns `/v2/applications/${index}/boxes` + */ + path() { + return `/v2/applications/${this.index}/boxes`; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const appID = 1234; + * + * const responsePage1 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .do(); + * const boxNamesPage1 = responsePage1.boxes.map(box => box.name); + * + * const responsePage2 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .nextToken(responsePage1.nextToken) + * .do(); + * const boxNamesPage2 = responsePage2.boxes.map(box => box.name); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(next: string) { + this.query.next = next; + return this; + } + + /** + * Limit results for pagination. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const boxesResponse = await indexerClient + * .searchForApplicationBoxes(1234) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): BoxesResponse { + return BoxesResponse.from_obj_for_encoding(body); + } +} diff --git a/src/client/v2/indexer/searchForApplications.ts b/src/client/v2/indexer/searchForApplications.ts new file mode 100644 index 0000000..abf122f --- /dev/null +++ b/src/client/v2/indexer/searchForApplications.ts @@ -0,0 +1,134 @@ +import JSONRequest from '../jsonrequest'; + +/** + * Returns information about indexed applications. + * + * #### Example + * ```typescript + * const apps = await indexerClient.searchForApplications().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applications) + * @category GET + */ +export default class SearchForApplications extends JSONRequest { + /** + * @returns `/v2/applications` + */ + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/applications'; + } + + /** + * Application ID for filter, as int + * + * #### Example + * ```typescript + * const appId = 60553466; + * const apps = await indexerClient + * .searchForApplications() + * .index(appId) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupApplications(appId).do()` + * @param index + * @category query + */ + index(index: number) { + this.query['application-id'] = index; + return this; + } + + /** + * Creator for filter, as string + * + * #### Example + * ```typescript + * const creator = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const apps = await indexerClient + * .searchForApplications() + * .creator(creator) + * .do(); + * ``` + * @param creator + * @category query + */ + creator(creator: string) { + this.query.creator = creator; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const maxResults = 20; + * + * const appsPage1 = await indexerClient + * .searchForApplications() + * .limit(maxResults) + * .do(); + * + * const appsPage2 = await indexerClient + * .searchForApplications() + * .limit(maxResults) + * .nextToken(appsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(next: string) { + this.query.next = next; + return this; + } + + /** + * Limit results for pagination. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const apps = await indexerClient + * .searchForApplications() + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Includes all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example 1 + * ```typescript + * const apps = await indexerClient + * .searchForApplications() + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const apps = await indexerClient + * .searchForApplications() + * .includeAll() + * .do(); + * ``` + * + * @param value - default true when called without passing a value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } +} diff --git a/src/client/v2/indexer/searchForAssets.ts b/src/client/v2/indexer/searchForAssets.ts new file mode 100644 index 0000000..79fa8c6 --- /dev/null +++ b/src/client/v2/indexer/searchForAssets.ts @@ -0,0 +1,175 @@ +import JSONRequest from '../jsonrequest'; + +/** + * Returns information about indexed assets. + * + * #### Example + * ```typescript + * const assets = await indexerClient.searchForAssets().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assets) + * @category GET + */ +export default class SearchForAssets extends JSONRequest { + /** + * @returns `/v2/assets` + */ + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/assets'; + } + + /** + * Limit results for pagination. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const assets = await indexerClient + * .searchForAssets() + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Filter just assets with the given creator address. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const assets = await indexerClient + * .searchForAssets() + * .creator(address) + * .do(); + * ``` + * + * @param creator + * @category query + */ + creator(creator: string) { + this.query.creator = creator; + return this; + } + + /** + * Filter just assets with the given name. + * + * #### Example + * ```typescript + * const name = "Test Token"; + * const assets = await indexerClient + * .searchForAssets() + * .name(name) + * .do(); + * ``` + * + * @param name + * @category query + */ + name(name: string) { + this.query.name = name; + return this; + } + + /** + * Filter just assets with the given unit. + * + * #### Example + * ```typescript + * const unit = "test"; + * const assets = await indexerClient + * .searchForAssets() + * .unit(unit) + * .do(); + * ``` + * + * @param unit + * @category query + */ + unit(unit: string) { + this.query.unit = unit; + return this; + } + + /** + * Asset ID for filter, as int. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assets = await indexerClient + * .searchForAssets() + * .index(assetId) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupAssetByID(assetId).do();` + * @param index + * @category query + */ + index(index: number) { + this.query['asset-id'] = index; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const maxResults = 20; + * + * const assetsPage1 = await indexerClient + * .searchForAssets() + * .limit(maxResults) + * .do(); + * + * const assetsPage2 = await indexerClient + * .searchForAssets() + * .limit(maxResults) + * .nextToken(assetsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Includes all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example 1 + * ```typescript + * const assets = await indexerClient + * .searchForAssets() + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assets = await indexerClient + * .searchForAssets() + * .includeAll() + * .do(); + * ``` + * + * @param value - default true when called without passing a value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } +} diff --git a/src/client/v2/indexer/searchForTransactions.ts b/src/client/v2/indexer/searchForTransactions.ts new file mode 100644 index 0000000..7583059 --- /dev/null +++ b/src/client/v2/indexer/searchForTransactions.ts @@ -0,0 +1,435 @@ +import JSONRequest from '../jsonrequest'; +import { base64StringFunnel } from './lookupAccountTransactions'; + +/** + * Returns information about indexed transactions. + * + * #### Example + * ```typescript + * const txns = await indexerClient.searchForTransactions().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2transactions) + * @category GET + */ +export default class SearchForTransactions extends JSONRequest { + /** + * @returns `/v2/transactions` + */ + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/transactions'; + } + + /** + * Specifies a prefix which must be contained in the note field. + * + * #### Example + * ```typescript + * const notePrefixBase64Encoded = "Y3JlYXRl"; + * const txns = await indexerClient + * .searchForTransactions() + * .notePrefix(notePrefixBase64Encoded) + * .do(); + * ``` + * + * @param prefix - base64 string or uint8array + * @category query + */ + notePrefix(prefix: Uint8Array | string) { + this.query['note-prefix'] = base64StringFunnel(prefix); + return this; + } + + /** + * Type of transaction to filter with. + * + * #### Example + * ```typescript + * const txns = await indexerClient + * .searchForTransactions() + * .txType("keyreg") + * .do(); + * ``` + * + * @param type - one of `pay`, `keyreg`, `acfg`, `axfer`, `afrz`, `appl`, `stpf` + * @category query + */ + txType(type: string) { + this.query['tx-type'] = type; + return this; + } + + /** + * Type of signature to filter with. + * - sig: Standard + * - msig: MultiSig + * - lsig: LogicSig + * + * #### Example + * ```typescript + * const txns = await indexerClient + * .searchForTransactions() + * .sigType("sig") + * .do(); + * ``` + * + * @param type - one of `sig`, `msig`, `lsig` + * @category query + */ + sigType(type: string) { + this.query['sig-type'] = type; + return this; + } + + /** + * Lookup the specific transaction by ID. + * + * #### Example + * ```typescript + * const txId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const txns = await indexerClient + * .searchForTransactions() + * .txid(txId) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupTransactionByID(txnId).do()` + * @param txid + * @category query + */ + txid(txid: string) { + this.query.txid = txid; + return this; + } + + /** + * Include results for the specified round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const txns = await indexerClient + * .searchForTransactions() + * .round(targetBlock) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupBlock(targetBlock).do()` + * @param round + * @category query + */ + round(round: number) { + this.query.round = round; + return this; + } + + /** + * Include results at or after the specified min-round. + * + * #### Example + * ```typescript + * const minRound = 18309917; + * const txns = await indexerClient + * .searchForTransactions() + * .minRound(minRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + minRound(round: number) { + this.query['min-round'] = round; + return this; + } + + /** + * Include results at or before the specified max-round. + * + * #### Example + * ```typescript + * const maxRound = 18309917; + * const txns = await indexerClient + * .searchForTransactions() + * .maxRound(maxRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + maxRound(round: number) { + this.query['max-round'] = round; + return this; + } + + /** + * Asset ID to filter with. + * + * #### Example + * ```typescript + * const assetID = 163650; + * const txns = await indexerClient + * .searchForTransactions() + * .assetID(assetID) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupAssetTransactions(assetId).do()` + * @param id + * @category query + */ + assetID(id: number) { + this.query['asset-id'] = id; + return this; + } + + /** + * Maximum number of results to return. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const txns = await indexerClient + * .searchForTransactions() + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Include results before the given time. + * + * #### Example + * ```typescript + * const beforeTime = "2022-02-02T20:20:22.02Z"; + * const txns = await indexerClient + * .searchForTransactions() + * .beforeTime(beforeTime) + * .do(); + * ``` + * + * @param before - rfc3339 string + * @category query + */ + beforeTime(before: string) { + this.query['before-time'] = before; + return this; + } + + /** + * Include results after the given time. + * + * #### Example + * ```typescript + * const afterTime = "2022-10-21T00:00:11.55Z"; + * const txns = await indexerClient + * .searchForTransactions() + * .afterTime(afterTime) + * .do(); + * ``` + * + * @param after - rfc3339 string + * @category query + */ + afterTime(after: string) { + this.query['after-time'] = after; + return this; + } + + /** + * Filtered results should have an amount greater than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const minBalance = 300000; + * const txns = await indexerClient + * .searchForTransactions() + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const minBalance = 300000; + * const txns = await indexerClient + * .searchForTransactions() + * .assetID(assetID) + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * @remarks + * If you are looking for transactions with the currency amount greater than 0, simply construct the query without `currencyGreaterThan` because it doesn't accept `-1`, and passing the `0` `currency-greater-than` value would exclude transactions with a 0 amount. + * + * @param greater + * @category query + */ + currencyGreaterThan(greater: number) { + this.query['currency-greater-than'] = greater; + return this; + } + + /** + * Filtered results should have an amount less than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const maxBalance = 500000; + * const txns = await indexerClient + * .searchForTransactions() + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const maxBalance = 500000; + * const txns = await indexerClient + * .searchForTransactions() + * .assetID(assetID) + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * @param lesser + * @category query + */ + currencyLessThan(lesser: number) { + this.query['currency-less-than'] = lesser; + return this; + } + + /** + * Combined with address, defines what address to filter on, as string. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const role = "freeze-target"; + * const txns = await indexerClient + * .searchForTransactions() + * .address(address) + * .addressRole(role) + * .do(); + * ``` + * + * @param role - one of `sender`, `receiver`, `freeze-target` + * @category query + */ + addressRole(role: string) { + this.query['address-role'] = role; + return this; + } + + /** + * Only include transactions with this address in one of the transaction fields. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const txns = await indexerClient + * .searchForTransactions() + * .address(address) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupAccountTransactions(address).do()` + * @param address + * @category query + */ + address(address: string) { + this.query.address = address; + return this; + } + + /** + * Whether or not to consider the `close-to` field as a receiver when filtering transactions, as bool. Set to `true` to ignore `close-to`. + * + * #### Example + * ```typescript + * const txns = await indexerClient + * .searchForTransactions() + * .excludeCloseTo(true) + * .do(); + * ``` + * + * @param exclude + * @category query + */ + excludeCloseTo(exclude: boolean) { + this.query['exclude-close-to'] = exclude; + return this; + } + + /** + * The next page of results. + * + * #### Example + * ```typescript + * const maxResults = 25; + * + * const txnsPage1 = await indexerClient + * .searchForTransactions() + * .limit(maxResults) + * .do(); + * + * const txnsPage2 = await indexerClient + * .searchForTransactions() + * .limit(maxResults) + * .nextToken(txnsPage1["next-token"]) + * .do(); + * ``` + * + * @param nextToken - provided by the previous results + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Whether or not to include rekeying transactions. + * + * #### Example + * ```typescript + * const txns = await indexerClient + * .searchForTransactions() + * .rekeyTo(false) + * .do(); + * ``` + * + * @param rekeyTo + * @category query + */ + rekeyTo(rekeyTo: boolean) { + this.query['rekey-to'] = rekeyTo; + return this; + } + + /** + * Filter for this application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const txns = await indexerClient + * .searchForTransactions() + * .applicationID(appId) + * .do(); + * ``` + * + * @param applicationID + * @category query + */ + applicationID(applicationID: number) { + this.query['application-id'] = applicationID; + return this; + } +} diff --git a/src/client/v2/jsonrequest.ts b/src/client/v2/jsonrequest.ts new file mode 100644 index 0000000..5b1062d --- /dev/null +++ b/src/client/v2/jsonrequest.ts @@ -0,0 +1,103 @@ +import HTTPClient from '../client'; +import IntDecoding from '../../types/intDecoding'; + +/** + * Base abstract class for JSON requests. + * + * Data: The type returned from the `do()` method + * + * Body: The structure of the response's body + */ +export default abstract class JSONRequest< + Data = Record, + Body = Data | Uint8Array +> { + c: HTTPClient; + query: Record; + intDecoding: IntDecoding; + + /** + * @param client - HTTPClient object. + * @param intDecoding - The method to use + * for decoding integers from this request's response. See the setIntDecoding method for more + * details. + */ + constructor(client: HTTPClient, intDecoding?: IntDecoding) { + this.c = client; + this.query = {}; + this.intDecoding = intDecoding || IntDecoding.DEFAULT; + } + + /** + * @returns The path of this request. + * @category JSONRequest + */ + abstract path(): string; + + /** + * Prepare a JSON response before returning it. + * + * Use this method to change and restructure response + * data as needed after receiving it from the `do()` method. + * @param body - Response body received + * @category JSONRequest + */ + // eslint-disable-next-line class-methods-use-this + prepare(body: Body): Data { + return (body as unknown) as Data; + } + + /** + * Execute the request. + * @param headers - Additional headers to send in the request. Optional. + * @returns A promise which resolves to the parsed response data. + * @category JSONRequest + */ + async do(headers: Record = {}): Promise { + const jsonOptions: Record = {}; + if (this.intDecoding !== 'default') { + jsonOptions.intDecoding = this.intDecoding; + } + const res = await this.c.get(this.path(), this.query, headers, jsonOptions); + return this.prepare(res.body); + } + + /** + * Execute the request, but do not process the response data in any way. + * @param headers - Additional headers to send in the request. Optional. + * @returns A promise which resolves to the raw response data, exactly as returned by the server. + * @category JSONRequest + */ + async doRaw(headers: Record = {}): Promise { + const res = await this.c.get(this.path(), this.query, headers, {}, false); + return res.body; + } + + /** + * Configure how integers in this request's JSON response will be decoded. + * + * The options are: + * * "default": Integers will be decoded according to JSON.parse, meaning they will all be + * Numbers and any values greater than Number.MAX_SAFE_INTEGER will lose precision. + * * "safe": All integers will be decoded as Numbers, but if any values are greater than + * Number.MAX_SAFE_INTEGER an error will be thrown. + * * "mixed": Integers will be decoded as Numbers if they are less than or equal to + * Number.MAX_SAFE_INTEGER, otherwise they will be decoded as BigInts. + * * "bigint": All integers will be decoded as BigInts. + * + * @param method - The method to use when parsing the + * response for this request. Must be one of "default", "safe", "mixed", or "bigint". + * @category JSONRequest + */ + setIntDecoding(method: IntDecoding) { + if ( + method !== 'default' && + method !== 'safe' && + method !== 'mixed' && + method !== 'bigint' + ) + throw new Error(`Invalid method for int decoding: ${method}`); + this.intDecoding = method; + return this; + } +} diff --git a/src/client/v2/serviceClient.ts b/src/client/v2/serviceClient.ts new file mode 100644 index 0000000..14fae29 --- /dev/null +++ b/src/client/v2/serviceClient.ts @@ -0,0 +1,89 @@ +import HTTPClient from '../client'; +import IntDecoding from '../../types/intDecoding'; +import { BaseHTTPClient } from '../baseHTTPClient'; +import { TokenHeader } from '../urlTokenBaseHTTPClient'; + +export type TokenHeaderIdentifier = + | 'X-Indexer-API-Token' + | 'X-KMD-API-Token' + | 'X-Algo-API-Token' + | string; + +/** + * Convert a token string to a token header + * @param token - The token string + * @param headerIdentifier - An identifier for the token header + */ +function convertTokenStringToTokenHeader( + token: string = '', + headerIdentifier: TokenHeaderIdentifier +): TokenHeader { + const tokenHeader = {}; + if (token === '') { + return tokenHeader; + } + tokenHeader[headerIdentifier] = token; + return tokenHeader as TokenHeader; +} + +function isBaseHTTPClient( + tbc: string | TokenHeader | BaseHTTPClient +): tbc is BaseHTTPClient { + return typeof (tbc as BaseHTTPClient).get === 'function'; +} + +/** + * Abstract service client to encapsulate shared AlgodClient and IndexerClient logic + */ +export default abstract class ServiceClient { + /** @ignore */ + c: HTTPClient; + /** @ignore */ + intDecoding: IntDecoding; + + constructor( + tokenHeaderIdentifier: TokenHeaderIdentifier, + tokenHeaderOrStrOrBaseClient: string | TokenHeader | BaseHTTPClient, + baseServer: string, + port?: string | number, + defaultHeaders: Record = {} + ) { + if (isBaseHTTPClient(tokenHeaderOrStrOrBaseClient)) { + // we are using a base client + this.c = new HTTPClient(tokenHeaderOrStrOrBaseClient); + } else { + // Accept token header as string or object + // - workaround to allow backwards compatibility for multiple headers + let tokenHeader: TokenHeader; + if (typeof tokenHeaderOrStrOrBaseClient === 'string') { + tokenHeader = convertTokenStringToTokenHeader( + tokenHeaderOrStrOrBaseClient, + tokenHeaderIdentifier + ); + } else { + tokenHeader = tokenHeaderOrStrOrBaseClient; + } + + this.c = new HTTPClient(tokenHeader, baseServer, port, defaultHeaders); + } + + this.intDecoding = IntDecoding.DEFAULT; + } + + /** + * Set the default int decoding method for all JSON requests this client creates. + * @param method - \{"default" | "safe" | "mixed" | "bigint"\} method The method to use when parsing the + * response for request. Must be one of "default", "safe", "mixed", or "bigint". See + * JSONRequest.setIntDecoding for more details about what each method does. + */ + setIntEncoding(method: IntDecoding) { + this.intDecoding = method; + } + + /** + * Get the default int decoding method for all JSON requests this client creates. + */ + getIntEncoding() { + return this.intDecoding; + } +} diff --git a/src/composer.ts b/src/composer.ts new file mode 100644 index 0000000..fb576e3 --- /dev/null +++ b/src/composer.ts @@ -0,0 +1,808 @@ +import { Buffer } from 'buffer'; +import { + ABIAddressType, + abiCheckTransactionType, + ABIMethod, + ABIReferenceType, + ABITupleType, + ABIType, + abiTypeIsReference, + abiTypeIsTransaction, + ABIUintType, + ABIValue, +} from './abi'; +import Algodv2 from './client/v2/algod/algod'; +import { + SimulateResponse, + SimulateRequest, + SimulateRequestTransactionGroup, +} from './client/v2/algod/models/types'; +import { EncodedSignedTransaction } from './types'; +import { assignGroupID } from './group'; +import { makeApplicationCallTxnFromObject } from './makeTxn'; +import { + isTransactionWithSigner, + TransactionSigner, + TransactionWithSigner, +} from './signer'; +import { decodeSignedTransaction, Transaction } from './transaction'; +import { + BoxReference, + OnApplicationComplete, + SuggestedParams, +} from './types/transactions/base'; +import { waitForConfirmation } from './wait'; +import * as encoding from './encoding/encoding'; + +// First 4 bytes of SHA-512/256 hash of "return" +const RETURN_PREFIX = Buffer.from([21, 31, 124, 117]); + +// The maximum number of arguments for an application call transaction +const MAX_APP_ARGS = 16; + +export type ABIArgument = ABIValue | TransactionWithSigner; + +/** Represents the output from a successful ABI method call. */ +export interface ABIResult { + /** The TxID of the transaction that invoked the ABI method call. */ + txID: string; + /** + * The raw bytes of the return value from the ABI method call. This will be empty if the method + * does not return a value (return type "void"). + */ + rawReturnValue: Uint8Array; + /** + * The method that was called for this result + */ + method: ABIMethod; + /** + * The return value from the ABI method call. This will be undefined if the method does not return + * a value (return type "void"), or if the SDK was unable to decode the returned value. + */ + returnValue?: ABIValue; + /** If the SDK was unable to decode a return value, the error will be here. */ + decodeError?: Error; + /** The pending transaction information from the method transaction */ + txInfo?: Record; +} + +export enum AtomicTransactionComposerStatus { + /** The atomic group is still under construction. */ + BUILDING, + + /** The atomic group has been finalized, but not yet signed. */ + BUILT, + + /** The atomic group has been finalized and signed, but not yet submitted to the network. */ + SIGNED, + + /** The atomic group has been finalized, signed, and submitted to the network. */ + SUBMITTED, + + /** The atomic group has been finalized, signed, submitted, and successfully committed to a block. */ + COMMITTED, +} + +/** + * Add a value to an application call's foreign array. The addition will be as compact as possible, + * and this function will return an index that can be used to reference `valueToAdd` in `array`. + * + * @param valueToAdd - The value to add to the array. If this value is already present in the array, + * it will not be added again. Instead, the existing index will be returned. + * @param array - The existing foreign array. This input may be modified to append `valueToAdd`. + * @param zeroValue - If provided, this value indicated two things: the 0 value is special for this + * array, so all indexes into `array` must start at 1; additionally, if `valueToAdd` equals + * `zeroValue`, then `valueToAdd` will not be added to the array, and instead the 0 indexes will + * be returned. + * @returns An index that can be used to reference `valueToAdd` in `array`. + */ +function populateForeignArray( + valueToAdd: Type, + array: Type[], + zeroValue?: Type +): number { + if (zeroValue != null && valueToAdd === zeroValue) { + return 0; + } + + const offset = zeroValue == null ? 0 : 1; + + for (let i = 0; i < array.length; i++) { + if (valueToAdd === array[i]) { + return i + offset; + } + } + + array.push(valueToAdd); + return array.length - 1 + offset; +} + +/** A class used to construct and execute atomic transaction groups */ +export class AtomicTransactionComposer { + /** The maximum size of an atomic transaction group. */ + static MAX_GROUP_SIZE: number = 16; + + private status = AtomicTransactionComposerStatus.BUILDING; + private transactions: TransactionWithSigner[] = []; + private methodCalls: Map = new Map(); + private signedTxns: Uint8Array[] = []; + private txIDs: string[] = []; + + /** + * Get the status of this composer's transaction group. + */ + getStatus(): AtomicTransactionComposerStatus { + return this.status; + } + + /** + * Get the number of transactions currently in this atomic group. + */ + count(): number { + return this.transactions.length; + } + + /** + * Create a new composer with the same underlying transactions. The new composer's status will be + * BUILDING, so additional transactions may be added to it. + */ + clone(): AtomicTransactionComposer { + const theClone = new AtomicTransactionComposer(); + + theClone.transactions = this.transactions.map(({ txn, signer }) => ({ + // not quite a deep copy, but good enough for our purposes (modifying txn.group in buildGroup) + txn: Transaction.from_obj_for_encoding({ + ...txn.get_obj_for_encoding(), + // erase the group ID + grp: undefined, + }), + signer, + })); + theClone.methodCalls = new Map(this.methodCalls); + + return theClone; + } + + /** + * Add a transaction to this atomic group. + * + * An error will be thrown if the transaction has a nonzero group ID, the composer's status is + * not BUILDING, or if adding this transaction causes the current group to exceed MAX_GROUP_SIZE. + */ + addTransaction(txnAndSigner: TransactionWithSigner): void { + if (this.status !== AtomicTransactionComposerStatus.BUILDING) { + throw new Error( + 'Cannot add transactions when composer status is not BUILDING' + ); + } + + if (this.transactions.length === AtomicTransactionComposer.MAX_GROUP_SIZE) { + throw new Error( + `Adding an additional transaction exceeds the maximum atomic group size of ${AtomicTransactionComposer.MAX_GROUP_SIZE}` + ); + } + + if (txnAndSigner.txn.group && txnAndSigner.txn.group.some((v) => v !== 0)) { + throw new Error('Cannot add a transaction with nonzero group ID'); + } + + this.transactions.push(txnAndSigner); + } + + /** + * Add a smart contract method call to this atomic group. + * + * An error will be thrown if the composer's status is not BUILDING, if adding this transaction + * causes the current group to exceed MAX_GROUP_SIZE, or if the provided arguments are invalid + * for the given method. + */ + addMethodCall({ + appID, + method, + methodArgs, + sender, + suggestedParams, + onComplete, + approvalProgram, + clearProgram, + numGlobalInts, + numGlobalByteSlices, + numLocalInts, + numLocalByteSlices, + extraPages, + appAccounts, + appForeignApps, + appForeignAssets, + boxes, + note, + lease, + rekeyTo, + signer, + }: { + /** The ID of the smart contract to call. Set this to 0 to indicate an application creation call. */ + appID: number; + /** The method to call on the smart contract */ + method: ABIMethod; + /** The arguments to include in the method call. If omitted, no arguments will be passed to the method. */ + methodArgs?: ABIArgument[]; + /** The address of the sender of this application call */ + sender: string; + /** Transactions params to use for this application call */ + suggestedParams: SuggestedParams; + /** The OnComplete action to take for this application call. If omitted, OnApplicationComplete.NoOpOC will be used. */ + onComplete?: OnApplicationComplete; + /** The approval program for this application call. Only set this if this is an application creation call, or if onComplete is OnApplicationComplete.UpdateApplicationOC */ + approvalProgram?: Uint8Array; + /** The clear program for this application call. Only set this if this is an application creation call, or if onComplete is OnApplicationComplete.UpdateApplicationOC */ + clearProgram?: Uint8Array; + /** The global integer schema size. Only set this if this is an application creation call. */ + numGlobalInts?: number; + /** The global byte slice schema size. Only set this if this is an application creation call. */ + numGlobalByteSlices?: number; + /** The local integer schema size. Only set this if this is an application creation call. */ + numLocalInts?: number; + /** The local byte slice schema size. Only set this if this is an application creation call. */ + numLocalByteSlices?: number; + /** The number of extra pages to allocate for the application's programs. Only set this if this is an application creation call. If omitted, defaults to 0. */ + extraPages?: number; + /** Array of Address strings that represent external accounts supplied to this application. If accounts are provided here, the accounts specified in the method args will appear after these. */ + appAccounts?: string[]; + /** Array of App ID numbers that represent external apps supplied to this application. If apps are provided here, the apps specified in the method args will appear after these. */ + appForeignApps?: number[]; + /** Array of Asset ID numbers that represent external assets supplied to this application. If assets are provided here, the assets specified in the method args will appear after these. */ + appForeignAssets?: number[]; + /** The box references for this application call */ + boxes?: BoxReference[]; + /** The note value for this application call */ + note?: Uint8Array; + /** The lease value for this application call */ + lease?: Uint8Array; + /** If provided, the address that the sender will be rekeyed to at the conclusion of this application call */ + rekeyTo?: string; + /** A transaction signer that can authorize this application call from sender */ + signer: TransactionSigner; + }): void { + if (this.status !== AtomicTransactionComposerStatus.BUILDING) { + throw new Error( + 'Cannot add transactions when composer status is not BUILDING' + ); + } + + if ( + this.transactions.length + method.txnCount() > + AtomicTransactionComposer.MAX_GROUP_SIZE + ) { + throw new Error( + `Adding additional transactions exceeds the maximum atomic group size of ${AtomicTransactionComposer.MAX_GROUP_SIZE}` + ); + } + + if (appID === 0) { + if ( + approvalProgram == null || + clearProgram == null || + numGlobalInts == null || + numGlobalByteSlices == null || + numLocalInts == null || + numLocalByteSlices == null + ) { + throw new Error( + 'One of the following required parameters for application creation is missing: approvalProgram, clearProgram, numGlobalInts, numGlobalByteSlices, numLocalInts, numLocalByteSlices' + ); + } + } else if (onComplete === OnApplicationComplete.UpdateApplicationOC) { + if (approvalProgram == null || clearProgram == null) { + throw new Error( + 'One of the following required parameters for OnApplicationComplete.UpdateApplicationOC is missing: approvalProgram, clearProgram' + ); + } + if ( + numGlobalInts != null || + numGlobalByteSlices != null || + numLocalInts != null || + numLocalByteSlices != null || + extraPages != null + ) { + throw new Error( + 'One of the following application creation parameters were set on a non-creation call: numGlobalInts, numGlobalByteSlices, numLocalInts, numLocalByteSlices, extraPages' + ); + } + } else if ( + approvalProgram != null || + clearProgram != null || + numGlobalInts != null || + numGlobalByteSlices != null || + numLocalInts != null || + numLocalByteSlices != null || + extraPages != null + ) { + throw new Error( + 'One of the following application creation parameters were set on a non-creation call: approvalProgram, clearProgram, numGlobalInts, numGlobalByteSlices, numLocalInts, numLocalByteSlices, extraPages' + ); + } + + if (methodArgs == null) { + // eslint-disable-next-line no-param-reassign + methodArgs = []; + } + + if (methodArgs.length !== method.args.length) { + throw new Error( + `Incorrect number of method arguments. Expected ${method.args.length}, got ${methodArgs.length}` + ); + } + + let basicArgTypes: ABIType[] = []; + let basicArgValues: ABIValue[] = []; + const txnArgs: TransactionWithSigner[] = []; + const refArgTypes: ABIReferenceType[] = []; + const refArgValues: ABIValue[] = []; + const refArgIndexToBasicArgIndex: Map = new Map(); + // TODO: Box encoding for ABI + const boxReferences: BoxReference[] = !boxes ? [] : boxes; + + for (let i = 0; i < methodArgs.length; i++) { + let argType = method.args[i].type; + const argValue = methodArgs[i]; + + if (abiTypeIsTransaction(argType)) { + if ( + !isTransactionWithSigner(argValue) || + !abiCheckTransactionType(argType, argValue.txn) + ) { + throw new Error( + `Expected ${argType} TransactionWithSigner for argument at index ${i}` + ); + } + if (argValue.txn.group && argValue.txn.group.some((v) => v !== 0)) { + throw new Error('Cannot add a transaction with nonzero group ID'); + } + txnArgs.push(argValue); + continue; + } + + if (isTransactionWithSigner(argValue)) { + throw new Error( + `Expected non-transaction value for argument at index ${i}` + ); + } + + if (abiTypeIsReference(argType)) { + refArgIndexToBasicArgIndex.set( + refArgTypes.length, + basicArgTypes.length + ); + refArgTypes.push(argType); + refArgValues.push(argValue); + // treat the reference as a uint8 for encoding purposes + argType = new ABIUintType(8); + } + + if (typeof argType === 'string') { + throw new Error(`Unknown ABI type: ${argType}`); + } + + basicArgTypes.push(argType); + basicArgValues.push(argValue); + } + + const resolvedRefIndexes: number[] = []; + const foreignAccounts: string[] = + appAccounts == null ? [] : appAccounts.slice(); + const foreignApps: number[] = + appForeignApps == null ? [] : appForeignApps.slice(); + const foreignAssets: number[] = + appForeignAssets == null ? [] : appForeignAssets.slice(); + for (let i = 0; i < refArgTypes.length; i++) { + const refType = refArgTypes[i]; + const refValue = refArgValues[i]; + let resolved = 0; + + switch (refType) { + case ABIReferenceType.account: { + const addressType = new ABIAddressType(); + const address = addressType.decode(addressType.encode(refValue)); + resolved = populateForeignArray(address, foreignAccounts, sender); + break; + } + case ABIReferenceType.application: { + const uint64Type = new ABIUintType(64); + const refAppID = uint64Type.decode(uint64Type.encode(refValue)); + if (refAppID > Number.MAX_SAFE_INTEGER) { + throw new Error( + `Expected safe integer for application value, got ${refAppID}` + ); + } + resolved = populateForeignArray(Number(refAppID), foreignApps, appID); + break; + } + case ABIReferenceType.asset: { + const uint64Type = new ABIUintType(64); + const refAssetID = uint64Type.decode(uint64Type.encode(refValue)); + if (refAssetID > Number.MAX_SAFE_INTEGER) { + throw new Error( + `Expected safe integer for asset value, got ${refAssetID}` + ); + } + resolved = populateForeignArray(Number(refAssetID), foreignAssets); + break; + } + default: + throw new Error(`Unknown reference type: ${refType}`); + } + + resolvedRefIndexes.push(resolved); + } + + for (let i = 0; i < resolvedRefIndexes.length; i++) { + const basicArgIndex = refArgIndexToBasicArgIndex.get(i); + basicArgValues[basicArgIndex] = resolvedRefIndexes[i]; + } + + if (basicArgTypes.length > MAX_APP_ARGS - 1) { + const lastArgTupleTypes = basicArgTypes.slice(MAX_APP_ARGS - 2); + const lastArgTupleValues = basicArgValues.slice(MAX_APP_ARGS - 2); + + basicArgTypes = basicArgTypes.slice(0, MAX_APP_ARGS - 2); + basicArgValues = basicArgValues.slice(0, MAX_APP_ARGS - 2); + + basicArgTypes.push(new ABITupleType(lastArgTupleTypes)); + basicArgValues.push(lastArgTupleValues); + } + + const appArgsEncoded: Uint8Array[] = [method.getSelector()]; + for (let i = 0; i < basicArgTypes.length; i++) { + appArgsEncoded.push(basicArgTypes[i].encode(basicArgValues[i])); + } + + const appCall = { + txn: makeApplicationCallTxnFromObject({ + from: sender, + appIndex: appID, + appArgs: appArgsEncoded, + accounts: foreignAccounts, + foreignApps, + foreignAssets, + boxes: boxReferences, + onComplete: + onComplete == null ? OnApplicationComplete.NoOpOC : onComplete, + approvalProgram, + clearProgram, + numGlobalInts, + numGlobalByteSlices, + numLocalInts, + numLocalByteSlices, + extraPages, + lease, + note, + rekeyTo, + suggestedParams, + }), + signer, + }; + + this.transactions.push(...txnArgs, appCall); + this.methodCalls.set(this.transactions.length - 1, method); + } + + /** + * Finalize the transaction group and returned the finalized transactions. + * + * The composer's status will be at least BUILT after executing this method. + */ + buildGroup(): TransactionWithSigner[] { + if (this.status === AtomicTransactionComposerStatus.BUILDING) { + if (this.transactions.length === 0) { + throw new Error('Cannot build a group with 0 transactions'); + } + if (this.transactions.length > 1) { + assignGroupID( + this.transactions.map((txnWithSigner) => txnWithSigner.txn) + ); + } + this.status = AtomicTransactionComposerStatus.BUILT; + } + return this.transactions; + } + + /** + * Obtain signatures for each transaction in this group. If signatures have already been obtained, + * this method will return cached versions of the signatures. + * + * The composer's status will be at least SIGNED after executing this method. + * + * An error will be thrown if signing any of the transactions fails. + * + * @returns A promise that resolves to an array of signed transactions. + */ + async gatherSignatures(): Promise { + if (this.status >= AtomicTransactionComposerStatus.SIGNED) { + return this.signedTxns; + } + + // retrieve built transactions and verify status is BUILT + const txnsWithSigners = this.buildGroup(); + const txnGroup = txnsWithSigners.map((txnWithSigner) => txnWithSigner.txn); + + const indexesPerSigner: Map = new Map(); + + for (let i = 0; i < txnsWithSigners.length; i++) { + const { signer } = txnsWithSigners[i]; + + if (!indexesPerSigner.has(signer)) { + indexesPerSigner.set(signer, []); + } + + indexesPerSigner.get(signer).push(i); + } + + const orderedSigners = Array.from(indexesPerSigner); + + const batchedSigs = await Promise.all( + orderedSigners.map(([signer, indexes]) => signer(txnGroup, indexes)) + ); + + const signedTxns: Array = txnsWithSigners.map( + () => null + ); + + for ( + let signerIndex = 0; + signerIndex < orderedSigners.length; + signerIndex++ + ) { + const indexes = orderedSigners[signerIndex][1]; + const sigs = batchedSigs[signerIndex]; + + for (let i = 0; i < indexes.length; i++) { + signedTxns[indexes[i]] = sigs[i]; + } + } + + if (!signedTxns.every((sig) => sig != null)) { + throw new Error(`Missing signatures. Got ${signedTxns}`); + } + + const txIDs = signedTxns.map((stxn, index) => { + try { + return decodeSignedTransaction(stxn).txn.txID(); + } catch (err) { + throw new Error( + `Cannot decode signed transaction at index ${index}. ${err}` + ); + } + }); + + this.signedTxns = signedTxns; + this.txIDs = txIDs; + this.status = AtomicTransactionComposerStatus.SIGNED; + + return signedTxns; + } + + /** + * Send the transaction group to the network, but don't wait for it to be committed to a block. An + * error will be thrown if submission fails. + * + * The composer's status must be SUBMITTED or lower before calling this method. If submission is + * successful, this composer's status will update to SUBMITTED. + * + * Note: a group can only be submitted again if it fails. + * + * @param client - An Algodv2 client + * + * @returns A promise that, upon success, resolves to a list of TxIDs of the submitted transactions. + */ + async submit(client: Algodv2): Promise { + if (this.status > AtomicTransactionComposerStatus.SUBMITTED) { + throw new Error('Transaction group cannot be resubmitted'); + } + + const stxns = await this.gatherSignatures(); + + await client.sendRawTransaction(stxns).do(); + + this.status = AtomicTransactionComposerStatus.SUBMITTED; + + return this.txIDs; + } + + /** + * Simulates the transaction group in the network. + * + * The composer will try to sign any transactions in the group, then simulate + * the results. + * Simulating the group will not change the composer's status. + * + * @param client - An Algodv2 client + * @param request - SimulateRequest with options in simulation. + * If provided, the request's transaction group will be overrwritten by the composer's group, + * only simulation related options will be used. + * + * @returns A promise that, upon success, resolves to an object containing an + * array of results containing one element for each method call transaction + * in this group (ABIResult[]) and the SimulateResponse object. + */ + async simulate( + client: Algodv2, + request?: SimulateRequest + ): Promise<{ + methodResults: ABIResult[]; + simulateResponse: SimulateResponse; + }> { + if (this.status > AtomicTransactionComposerStatus.SUBMITTED) { + throw new Error( + 'Simulated Transaction group has already been submitted to the network' + ); + } + + const stxns = await this.gatherSignatures(); + const txnObjects: EncodedSignedTransaction[] = stxns.map( + (stxn) => encoding.decode(stxn) as EncodedSignedTransaction + ); + + const currentRequest: SimulateRequest = + request == null ? new SimulateRequest({ txnGroups: [] }) : request; + + currentRequest.txnGroups = [ + new SimulateRequestTransactionGroup({ + txns: txnObjects, + }), + ]; + + const simulateResponse = await client + .simulateTransactions(currentRequest) + .do(); + + // Parse method response + const methodResults: ABIResult[] = []; + for (const [txnIndex, method] of this.methodCalls) { + const txID = this.txIDs[txnIndex]; + const pendingInfo = + simulateResponse.txnGroups[0].txnResults[txnIndex].txnResult; + + const methodResult: ABIResult = { + txID, + rawReturnValue: new Uint8Array(), + method, + }; + + methodResults.push( + AtomicTransactionComposer.parseMethodResponse( + method, + methodResult, + pendingInfo.get_obj_for_encoding() + ) + ); + } + + return { methodResults, simulateResponse }; + } + + /** + * Send the transaction group to the network and wait until it's committed to a block. An error + * will be thrown if submission or execution fails. + * + * The composer's status must be SUBMITTED or lower before calling this method, since execution is + * only allowed once. If submission is successful, this composer's status will update to SUBMITTED. + * If the execution is also successful, this composer's status will update to COMMITTED. + * + * Note: a group can only be submitted again if it fails. + * + * @param client - An Algodv2 client + * @param waitRounds - The maximum number of rounds to wait for transaction confirmation + * + * @returns A promise that, upon success, resolves to an object containing the confirmed round for + * this transaction, the txIDs of the submitted transactions, and an array of results containing + * one element for each method call transaction in this group. + */ + async execute( + client: Algodv2, + waitRounds: number + ): Promise<{ + confirmedRound: number; + txIDs: string[]; + methodResults: ABIResult[]; + }> { + if (this.status === AtomicTransactionComposerStatus.COMMITTED) { + throw new Error( + 'Transaction group has already been executed successfully' + ); + } + + const txIDs = await this.submit(client); + this.status = AtomicTransactionComposerStatus.SUBMITTED; + + const firstMethodCallIndex = this.transactions.findIndex((_, index) => + this.methodCalls.has(index) + ); + const indexToWaitFor = + firstMethodCallIndex === -1 ? 0 : firstMethodCallIndex; + const confirmedTxnInfo = await waitForConfirmation( + client, + txIDs[indexToWaitFor], + waitRounds + ); + this.status = AtomicTransactionComposerStatus.COMMITTED; + + const confirmedRound: number = confirmedTxnInfo['confirmed-round']; + + const methodResults: ABIResult[] = []; + + for (const [txnIndex, method] of this.methodCalls) { + const txID = txIDs[txnIndex]; + + let methodResult: ABIResult = { + txID, + rawReturnValue: new Uint8Array(), + method, + }; + + try { + const pendingInfo = + txnIndex === firstMethodCallIndex + ? confirmedTxnInfo + : // eslint-disable-next-line no-await-in-loop + await client.pendingTransactionInformation(txID).do(); + + methodResult = AtomicTransactionComposer.parseMethodResponse( + method, + methodResult, + pendingInfo + ); + } catch (err) { + methodResult.decodeError = err; + } + + methodResults.push(methodResult); + } + + return { + confirmedRound, + txIDs, + methodResults, + }; + } + + /** + * Parses a single ABI Method transaction log into a ABI result object. + * + * @param method + * @param methodResult + * @param pendingInfo + * @returns An ABIResult object + */ + static parseMethodResponse( + method: ABIMethod, + methodResult: ABIResult, + pendingInfo: Record + ): ABIResult { + const returnedResult: ABIResult = methodResult; + try { + returnedResult.txInfo = pendingInfo; + if (method.returns.type !== 'void') { + const logs: string[] = pendingInfo.logs || []; + if (logs.length === 0) { + throw new Error('App call transaction did not log a return value'); + } + + const lastLog = Buffer.from(logs[logs.length - 1], 'base64'); + if ( + lastLog.byteLength < 4 || + !lastLog.slice(0, 4).equals(RETURN_PREFIX) + ) { + throw new Error('App call transaction did not log a return value'); + } + + returnedResult.rawReturnValue = new Uint8Array(lastLog.slice(4)); + returnedResult.returnValue = method.returns.type.decode( + methodResult.rawReturnValue + ); + } + } catch (err) { + returnedResult.decodeError = err; + } + + return returnedResult; + } +} diff --git a/src/convert.ts b/src/convert.ts new file mode 100644 index 0000000..54f9b05 --- /dev/null +++ b/src/convert.ts @@ -0,0 +1,25 @@ +const MICROALGOS_TO_ALGOS_RATIO = 1e6; +export const INVALID_MICROALGOS_ERROR_MSG = + 'Microalgos should be positive and less than 2^53 - 1.'; + +/** + * microalgosToAlgos converts microalgos to algos + * @param microalgos - number + * @returns number + */ +export function microalgosToAlgos(microalgos: number) { + if (microalgos < 0 || !Number.isSafeInteger(microalgos)) { + throw new Error(INVALID_MICROALGOS_ERROR_MSG); + } + return microalgos / MICROALGOS_TO_ALGOS_RATIO; +} + +/** + * algosToMicroalgos converts algos to microalgos + * @param algos - number + * @returns number + */ +export function algosToMicroalgos(algos: number) { + const microalgos = algos * MICROALGOS_TO_ALGOS_RATIO; + return Math.round(microalgos); +} diff --git a/src/dryrun.ts b/src/dryrun.ts new file mode 100644 index 0000000..ce9739b --- /dev/null +++ b/src/dryrun.ts @@ -0,0 +1,469 @@ +import { Buffer } from 'buffer'; +import AlgodClient from './client/v2/algod/algod'; +import { + AccountStateDelta, + Application, + ApplicationParams, + ApplicationStateSchema, + DryrunRequest, + DryrunSource, + EvalDeltaKeyValue, + TealValue, +} from './client/v2/algod/models/types'; +import { SignedTransaction } from './transaction'; +import { TransactionType } from './types/transactions'; +import { encodeAddress, getApplicationAddress } from './encoding/address'; + +const defaultAppId = 1380011588; +const defaultMaxWidth = 30; + +// When writing the DryrunRequest object as msgpack the output needs to be the byte arrays not b64 string +interface AppParamsWithPrograms { + ['approval-program']: string | Uint8Array; + ['clear-state-program']: string | Uint8Array; + ['creator']: string; +} + +interface AppWithAppParams { + ['params']: AppParamsWithPrograms; +} + +function decodePrograms(ap: AppWithAppParams): AppWithAppParams { + // eslint-disable-next-line no-param-reassign + ap.params['approval-program'] = Buffer.from( + ap.params['approval-program'].toString(), + 'base64' + ); + // eslint-disable-next-line no-param-reassign + ap.params['clear-state-program'] = Buffer.from( + ap.params['clear-state-program'].toString(), + 'base64' + ); + + return ap; +} + +/** + * createDryrun takes an Algod Client (from algod.AlgodV2Client) and an array of Signed Transactions + * from (transaction.SignedTransaction) and creates a DryrunRequest object with relevant balances + * @param client - the AlgodClient to make requests against + * @param txns - the array of SignedTransaction to use for generating the DryrunRequest object + * @param protocolVersion - the string representing the protocol version to use + * @param latestTimestamp - the timestamp + * @returns the DryrunRequest object constructed from the SignedTransactions passed + */ +export async function createDryrun({ + client, + txns, + protocolVersion, + latestTimestamp, + round, + sources, +}: { + client: AlgodClient; + txns: SignedTransaction[]; + protocolVersion?: string; + latestTimestamp?: number | bigint; + round?: number | bigint; + sources?: DryrunSource[]; +}): Promise { + const appInfos = []; + const acctInfos = []; + + const apps: number[] = []; + const assets: number[] = []; + const accts: string[] = []; + + for (const t of txns) { + if (t.txn.type === TransactionType.appl) { + accts.push(encodeAddress(t.txn.from.publicKey)); + + if (t.txn.appAccounts) + accts.push(...t.txn.appAccounts.map((a) => encodeAddress(a.publicKey))); + + if (t.txn.appForeignApps) { + apps.push(...t.txn.appForeignApps); + accts.push( + ...t.txn.appForeignApps.map((aidx) => getApplicationAddress(aidx)) + ); + } + + if (t.txn.appForeignAssets) assets.push(...t.txn.appForeignAssets); + + // Create application, + if (t.txn.appIndex === undefined || t.txn.appIndex === 0) { + appInfos.push( + new Application({ + id: defaultAppId, + params: new ApplicationParams({ + creator: encodeAddress(t.txn.from.publicKey), + approvalProgram: t.txn.appApprovalProgram, + clearStateProgram: t.txn.appClearProgram, + localStateSchema: new ApplicationStateSchema({ + numUint: t.txn.appLocalInts, + numByteSlice: t.txn.appLocalByteSlices, + }), + globalStateSchema: new ApplicationStateSchema({ + numUint: t.txn.appGlobalInts, + numByteSlice: t.txn.appGlobalByteSlices, + }), + }), + }) + ); + } else { + apps.push(t.txn.appIndex); + accts.push(getApplicationAddress(t.txn.appIndex)); + } + } + } + + // Dedupe and add creator to accts array + const assetPromises = []; + for (const assetId of [...new Set(assets)]) { + assetPromises.push( + client + .getAssetByID(assetId) + .do() + .then((assetInfo) => { + accts.push(assetInfo.params.creator); + }) + ); + } + // Wait for assets to finish since we append to accts array + await Promise.all(assetPromises); + + // Dedupe and get app info for all apps + const appPromises = []; + for (const appId of [...new Set(apps)]) { + appPromises.push( + client + .getApplicationByID(appId) + .do() + .then((appInfo) => { + const ai = decodePrograms(appInfo as AppWithAppParams); + appInfos.push(ai); + accts.push(ai.params.creator); + }) + ); + } + await Promise.all(appPromises); + + const acctPromises = []; + for (const acct of [...new Set(accts)]) { + acctPromises.push( + client + .accountInformation(acct) + .do() + .then((acctInfo) => { + if ('created-apps' in acctInfo) { + // eslint-disable-next-line no-param-reassign + acctInfo['created-apps'] = acctInfo['created-apps'].map((app) => + decodePrograms(app) + ); + } + acctInfos.push(acctInfo); + }) + ); + } + await Promise.all(acctPromises); + + return new DryrunRequest({ + txns: txns.map((st) => ({ ...st, txn: st.txn.get_obj_for_encoding() })), + accounts: acctInfos, + apps: appInfos, + latestTimestamp, + round, + protocolVersion, + sources, + }); +} + +interface StackValueResponse { + type: number; + bytes: string; + uint: number; +} + +class DryrunStackValue { + type: number = 0; + bytes: string = ''; + uint: number = 0; + + constructor(sv: StackValueResponse) { + this.type = sv.type; + this.bytes = sv.bytes; + this.uint = sv.uint; + } + + toString(): string { + if (this.type === 1) { + return `0x${Buffer.from(this.bytes, 'base64').toString('hex')}`; + } + return this.uint.toString(); + } +} + +interface DryrunTraceLineResponse { + error: string; + line: number; + pc: number; + scratch: TealValue[]; + stack: StackValueResponse[]; +} + +class DryrunTraceLine { + error: string = ''; + line: number = 0; + pc: number = 0; + scratch: TealValue[] = []; + stack: DryrunStackValue[] = []; + + constructor(line: DryrunTraceLineResponse) { + this.error = line.error === undefined ? '' : line.error; + this.line = line.line; + this.pc = line.pc; + this.scratch = line.scratch; + this.stack = line.stack.map( + (sv: StackValueResponse) => new DryrunStackValue(sv) + ); + } +} + +class DryrunTrace { + trace: DryrunTraceLine[] = []; + constructor(t: DryrunTraceLineResponse[]) { + if (t == null) return; + this.trace = t.map((line) => new DryrunTraceLine(line)); + } +} + +interface DryrunTransactionResultResponse { + disassembly: string[]; + appCallMessages: string[] | undefined; + localDeltas: AccountStateDelta[] | undefined; + globalDelta: EvalDeltaKeyValue[] | undefined; + cost: number | undefined; + logicSigMessages: string[] | undefined; + logicSigDisassembly: string[] | undefined; + logs: string[] | undefined; + appCallTrace: DryrunTrace | undefined; + logicSigTrace: DryrunTrace | undefined; +} + +interface StackPrinterConfig { + maxValueWidth: number | undefined; + topOfStackFirst: boolean | undefined; +} + +function truncate(str: string, maxValueWidth: number): string { + if (str.length > maxValueWidth && maxValueWidth > 0) { + return `${str.slice(0, maxValueWidth)}...`; + } + return str; +} + +function scratchToString( + prevScratch: TealValue[], + currScratch: TealValue[] +): string { + if (currScratch.length === 0) return ''; + + let newScratchIdx = null; + for (let idx = 0; idx < currScratch.length; idx++) { + if (idx > prevScratch.length) { + newScratchIdx = idx; + continue; + } + + if (JSON.stringify(prevScratch[idx]) !== JSON.stringify(currScratch[idx])) { + newScratchIdx = idx; + } + } + + if (newScratchIdx == null) return ''; + + const newScratch = currScratch[newScratchIdx]; + if (newScratch.bytes.length > 0) { + return `${newScratchIdx} = 0x${Buffer.from( + newScratch.bytes, + 'base64' + ).toString('hex')}`; + } + return `${newScratchIdx} = ${newScratch.uint.toString()}`; +} + +function stackToString(stack: DryrunStackValue[], reverse: boolean): string { + const svs = reverse ? stack.reverse() : stack; + return `[${svs + .map((sv: DryrunStackValue) => { + switch (sv.type) { + case 1: + return `0x${Buffer.from(sv.bytes, 'base64').toString('hex')}`; + case 2: + return `${sv.uint.toString()}`; + default: + return ''; + } + }) + .join(', ')}]`; +} + +class DryrunTransactionResult { + disassembly: string[] = []; + appCallMessages: string[] | undefined = []; + localDeltas: AccountStateDelta[] | undefined = []; + globalDelta: EvalDeltaKeyValue[] | undefined = []; + cost: number | undefined = 0; + logicSigMessages: string[] | undefined = []; + logicSigDisassembly: string[] | undefined = []; + logs: string[] | undefined = []; + + appCallTrace: DryrunTrace | undefined = undefined; + logicSigTrace: DryrunTrace | undefined = undefined; + + required = ['disassembly']; + optionals = [ + 'app-call-messages', + 'local-deltas', + 'global-delta', + 'cost', + 'logic-sig-messages', + 'logic-sig-disassembly', + 'logs', + ]; + + traces = ['app-call-trace', 'logic-sig-trace']; + + constructor(dtr: DryrunTransactionResultResponse) { + this.disassembly = dtr.disassembly; + this.appCallMessages = dtr['app-call-messages']; + this.localDeltas = dtr['local-deltas']; + this.globalDelta = dtr['global-delta']; + this.cost = dtr.cost; + this.logicSigMessages = dtr['logic-sig-messages']; + this.logicSigDisassembly = dtr['logic-sig-disassembly']; + this.logs = dtr.logs; + this.appCallTrace = new DryrunTrace(dtr['app-call-trace']); + this.logicSigTrace = new DryrunTrace(dtr['logic-sig-trace']); + } + + appCallRejected(): boolean { + return ( + this.appCallMessages !== undefined && + this.appCallMessages.includes('REJECT') + ); + } + + logicSigRejected(): boolean { + return ( + this.logicSigMessages !== undefined && + this.logicSigMessages.includes('REJECT') + ); + } + + static trace( + drt: DryrunTrace, + disassembly: string[], + spc: StackPrinterConfig + ): string { + const maxWidth = spc.maxValueWidth || defaultMaxWidth; + + // Create the array of arrays, each sub array contains N columns + const lines = [['pc#', 'ln#', 'source', 'scratch', 'stack']]; + for (let idx = 0; idx < drt.trace.length; idx++) { + const { line, error, pc, scratch, stack } = drt.trace[idx]; + + const currScratch = scratch !== undefined ? scratch : []; + const prevScratch = + idx > 0 && drt.trace[idx - 1].scratch !== undefined + ? drt.trace[idx - 1].scratch + : []; + + const src = error === '' ? disassembly[line] : `!! ${error} !!`; + + lines.push([ + pc.toString().padEnd(3, ' '), + line.toString().padEnd(3, ' '), + truncate(src, maxWidth), + truncate(scratchToString(prevScratch, currScratch), maxWidth), + truncate(stackToString(stack, spc.topOfStackFirst), maxWidth), + ]); + } + + // Get the max length for each column + const maxLengths = lines.reduce((prev, curr) => { + const newVal = new Array(lines[0].length).fill(0); + for (let idx = 0; idx < prev.length; idx++) { + newVal[idx] = + curr[idx].length > prev[idx] ? curr[idx].length : prev[idx]; + } + return newVal; + }, new Array(lines[0].length).fill(0)); + + return `${lines + .map((line) => + line + .map((v, idx) => v.padEnd(maxLengths[idx] + 1, ' ')) + .join('|') + .trim() + ) + .join('\n')}\n`; + } + + appTrace(spc?: StackPrinterConfig): string { + if (this.appCallTrace === undefined || !this.disassembly) return ''; + + let conf = spc; + if (spc === undefined) + conf = { + maxValueWidth: defaultMaxWidth, + topOfStackFirst: false, + } as StackPrinterConfig; + + return DryrunTransactionResult.trace( + this.appCallTrace, + this.disassembly, + conf + ); + } + + lsigTrace(spc?: StackPrinterConfig): string { + if ( + this.logicSigTrace === undefined || + this.logicSigDisassembly === undefined + ) + return ''; + + let conf = spc; + if (spc === undefined) + conf = { + maxValueWidth: defaultMaxWidth, + topOfStackFirst: true, + } as StackPrinterConfig; + + return DryrunTransactionResult.trace( + this.logicSigTrace, + this.logicSigDisassembly, + conf + ); + } +} + +interface DryrunResultResponse { + ['error']: string; + ['protocol-version']: string; + ['txns']: DryrunTransactionResultResponse[]; +} + +export class DryrunResult { + error: string = ''; + protocolVersion: string = ''; + txns: DryrunTransactionResult[] = []; + constructor(drrResp: DryrunResultResponse) { + this.error = drrResp.error; + this.protocolVersion = drrResp['protocol-version']; + this.txns = drrResp.txns.map( + (txn: DryrunTransactionResultResponse) => new DryrunTransactionResult(txn) + ); + } +} diff --git a/src/encoding/address.ts b/src/encoding/address.ts new file mode 100644 index 0000000..b1e287e --- /dev/null +++ b/src/encoding/address.ts @@ -0,0 +1,192 @@ +import { Buffer } from 'buffer'; +import base32 from 'hi-base32'; +import * as nacl from '../nacl/naclWrappers'; +import * as utils from '../utils/utils'; +import { encodeUint64 } from './uint64'; +import { Address } from '../types/address'; +import { MultisigMetadata } from '../types/multisig'; + +const ALGORAND_ADDRESS_BYTE_LENGTH = 36; +const ALGORAND_CHECKSUM_BYTE_LENGTH = 4; +const ALGORAND_ADDRESS_LENGTH = 58; +export const ALGORAND_ZERO_ADDRESS_STRING = + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ'; + +// Convert "MultisigAddr" UTF-8 to byte array +const MULTISIG_PREIMG2ADDR_PREFIX = new Uint8Array([ + 77, + 117, + 108, + 116, + 105, + 115, + 105, + 103, + 65, + 100, + 100, + 114, +]); + +const APP_ID_PREFIX = Buffer.from('appID'); + +export const MALFORMED_ADDRESS_ERROR_MSG = 'address seems to be malformed'; +export const CHECKSUM_ADDRESS_ERROR_MSG = 'wrong checksum for address'; +export const INVALID_MSIG_VERSION_ERROR_MSG = 'invalid multisig version'; +export const INVALID_MSIG_THRESHOLD_ERROR_MSG = 'bad multisig threshold'; +export const INVALID_MSIG_PK_ERROR_MSG = + 'bad multisig public key - wrong length'; +export const UNEXPECTED_PK_LEN_ERROR_MSG = + 'nacl public key length is not 32 bytes'; + +/** + * decodeAddress takes an Algorand address in string form and decodes it into a Uint8Array. + * @param address - an Algorand address with checksum. + * @returns the decoded form of the address's public key and checksum + */ +export function decodeAddress(address: string): Address { + if (typeof address !== 'string' || address.length !== ALGORAND_ADDRESS_LENGTH) + throw new Error(MALFORMED_ADDRESS_ERROR_MSG); + + // try to decode + const decoded = base32.decode.asBytes(address.toString()); + // Sanity check + if (decoded.length !== ALGORAND_ADDRESS_BYTE_LENGTH) + throw new Error(MALFORMED_ADDRESS_ERROR_MSG); + + // Find publickey and checksum + const pk = new Uint8Array( + decoded.slice( + 0, + ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH + ) + ); + const cs = new Uint8Array( + decoded.slice(nacl.PUBLIC_KEY_LENGTH, ALGORAND_ADDRESS_BYTE_LENGTH) + ); + + // Compute checksum + const checksum = nacl + .genericHash(pk) + .slice( + nacl.HASH_BYTES_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH, + nacl.HASH_BYTES_LENGTH + ); + + // Check if the checksum and the address are equal + if (!utils.arrayEqual(checksum, cs)) + throw new Error(CHECKSUM_ADDRESS_ERROR_MSG); + + return { publicKey: pk, checksum: cs }; +} + +/** + * isValidAddress checks if a string is a valid Algorand address. + * @param address - an Algorand address with checksum. + * @returns true if valid, false otherwise + */ +export function isValidAddress(address: string) { + // Try to decode + try { + decodeAddress(address); + } catch (e) { + return false; + } + return true; +} + +/** + * encodeAddress takes an Algorand address as a Uint8Array and encodes it into a string with checksum. + * @param address - a raw Algorand address + * @returns the address and checksum encoded as a string. + */ +export function encodeAddress(address: Uint8Array) { + // compute checksum + const checksum = nacl + .genericHash(address) + .slice( + nacl.PUBLIC_KEY_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH, + nacl.PUBLIC_KEY_LENGTH + ); + const addr = base32.encode(utils.concatArrays(address, checksum)); + + return addr.toString().slice(0, ALGORAND_ADDRESS_LENGTH); // removing the extra '====' +} + +/** + * fromMultisigPreImg takes multisig parameters and returns a 32 byte typed array public key, + * representing an address that identifies the "exact group, version, and public keys" that are required for signing. + * Hash("MultisigAddr" || version uint8 || threshold uint8 || PK1 || PK2 || ...) + * Encoding this output yields a human readable address. + * @param version - multisig version + * @param threshold - multisig threshold + * @param pks - array of typed array public keys + */ +export function fromMultisigPreImg({ + version, + threshold, + pks, +}: Omit & { + pks: Uint8Array[]; +}) { + if (version !== 1 || version > 255 || version < 0) { + // ^ a tad redundant, but in case in the future version != 1, still check for uint8 + throw new Error(INVALID_MSIG_VERSION_ERROR_MSG); + } + if ( + threshold === 0 || + pks.length === 0 || + threshold > pks.length || + threshold > 255 + ) { + throw new Error(INVALID_MSIG_THRESHOLD_ERROR_MSG); + } + const pkLen = ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH; + if (pkLen !== nacl.PUBLIC_KEY_LENGTH) { + throw new Error(UNEXPECTED_PK_LEN_ERROR_MSG); + } + const merged = new Uint8Array( + MULTISIG_PREIMG2ADDR_PREFIX.length + 2 + pkLen * pks.length + ); + merged.set(MULTISIG_PREIMG2ADDR_PREFIX, 0); + merged.set([version], MULTISIG_PREIMG2ADDR_PREFIX.length); + merged.set([threshold], MULTISIG_PREIMG2ADDR_PREFIX.length + 1); + for (let i = 0; i < pks.length; i++) { + if (pks[i].length !== pkLen) { + throw new Error(INVALID_MSIG_PK_ERROR_MSG); + } + merged.set(pks[i], MULTISIG_PREIMG2ADDR_PREFIX.length + 2 + i * pkLen); + } + return new Uint8Array(nacl.genericHash(merged)); +} + +/** + * fromMultisigPreImgAddrs takes multisig parameters and returns a human readable Algorand address. + * This is equivalent to fromMultisigPreImg, but interfaces with encoded addresses. + * @param version - multisig version + * @param threshold - multisig threshold + * @param addrs - array of encoded addresses + */ +export function fromMultisigPreImgAddrs({ + version, + threshold, + addrs, +}: { + version: number; + threshold: number; + addrs: string[]; +}) { + const pks = addrs.map((addr) => decodeAddress(addr).publicKey); + return encodeAddress(fromMultisigPreImg({ version, threshold, pks })); +} + +/** + * Get the escrow address of an application. + * @param appID - The ID of the application. + * @returns The address corresponding to that application's escrow account. + */ +export function getApplicationAddress(appID: number | bigint): string { + const toBeSigned = utils.concatArrays(APP_ID_PREFIX, encodeUint64(appID)); + const hash = nacl.genericHash(toBeSigned); + return encodeAddress(new Uint8Array(hash)); +} diff --git a/src/encoding/bigint.ts b/src/encoding/bigint.ts new file mode 100644 index 0000000..cc03990 --- /dev/null +++ b/src/encoding/bigint.ts @@ -0,0 +1,35 @@ +import { Buffer } from 'buffer'; + +/** + * bigIntToBytes converts a BigInt to a big-endian Uint8Array for encoding. + * @param bi - The bigint to convert. + * @param size - The size of the resulting byte array. + * @returns A byte array containing the big-endian encoding of the input bigint + */ +export function bigIntToBytes(bi: bigint | number, size: number) { + let hex = bi.toString(16); + // Pad the hex with zeros so it matches the size in bytes + if (hex.length !== size * 2) { + hex = hex.padStart(size * 2, '0'); + } + const byteArray = new Uint8Array(hex.length / 2); + for (let i = 0, j = 0; i < hex.length / 2; i++, j += 2) { + byteArray[i] = parseInt(hex.slice(j, j + 2), 16); + } + return byteArray; +} + +/** + * bytesToBigInt produces a bigint from a binary representation. + * + * @param bytes - The Uint8Array to convert. + * @returns The bigint that was encoded in the input data. + */ +export function bytesToBigInt(bytes: Uint8Array) { + let res = BigInt(0); + const buf = Buffer.from(bytes); + for (let i = 0; i < bytes.length; i++) { + res = BigInt(Number(buf.readUIntBE(i, 1))) + res * BigInt(256); + } + return res; +} diff --git a/src/encoding/encoding.ts b/src/encoding/encoding.ts new file mode 100644 index 0000000..c770f57 --- /dev/null +++ b/src/encoding/encoding.ts @@ -0,0 +1,67 @@ +/** + * This file is a wrapper of msgpack.js. + * The wrapper was written in order to ensure correct encoding of Algorand Transaction and other formats. + * In particular, it matches go-algorand blockchain client, written in go (https://www.github.com/algorand/go-algorand. + * Algorand's msgpack encoding follows to following rules - + * 1. Every integer must be encoded to the smallest type possible (0-255-\>8bit, 256-65535-\>16bit, etx) + * 2. All fields names must be sorted + * 3. All empty and 0 fields should be omitted + * 4. Every positive number must be encoded as uint + * 5. Binary blob should be used for binary data and string for strings + * */ + +import * as msgpack from 'algo-msgpack-with-bigint'; + +// Errors +export const ERROR_CONTAINS_EMPTY_STRING = + 'The object contains empty or 0 values. First empty or 0 value encountered during encoding: '; + +/** + * containsEmpty returns true if any of the object's values are empty, false otherwise. + * Empty arrays considered empty + * @param obj - The object to check + * @returns \{true, empty key\} if contains empty, \{false, undefined\} otherwise + */ +function containsEmpty(obj: Record) { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + if (!obj[key] || obj[key].length === 0) { + return { containsEmpty: true, firstEmptyKey: key }; + } + } + } + return { containsEmpty: false, firstEmptyKey: undefined }; +} + +/** + * rawEncode encodes objects using msgpack, regardless of whether there are + * empty or 0 value fields. + * @param obj - a dictionary to be encoded. May or may not contain empty or 0 values. + * @returns msgpack representation of the object + */ +export function rawEncode(obj: Record) { + // enable the canonical option + const options = { sortKeys: true }; + return msgpack.encode(obj, options); +} + +/** + * encode encodes objects using msgpack + * @param obj - a dictionary to be encoded. Must not contain empty or 0 values. + * @returns msgpack representation of the object + * @throws Error containing ERROR_CONTAINS_EMPTY_STRING if the object contains empty or zero values + */ +export function encode(obj: Record) { + // Check for empty values + const emptyCheck = containsEmpty(obj); + if (emptyCheck.containsEmpty) { + throw new Error(ERROR_CONTAINS_EMPTY_STRING + emptyCheck.firstEmptyKey); + } + + // enable the canonical option + return rawEncode(obj); +} + +export function decode(buffer: ArrayLike) { + return msgpack.decode(buffer); +} diff --git a/src/encoding/uint64.ts b/src/encoding/uint64.ts new file mode 100644 index 0000000..d190996 --- /dev/null +++ b/src/encoding/uint64.ts @@ -0,0 +1,90 @@ +import { concatArrays } from '../utils/utils'; + +// NOTE: at the moment we specifically do not use Buffer.writeBigUInt64BE and +// Buffer.readBigUInt64BE. This is because projects using webpack v4 +// automatically include an old version of the npm `buffer` package (v4.9.2 at +// the time of writing), and this old version does not have these methods. + +/** + * encodeUint64 converts an integer to its binary representation. + * @param num - The number to convert. This must be an unsigned integer less than + * 2^64. + * @returns An 8-byte typed array containing the big-endian encoding of the input + * integer. + */ +export function encodeUint64(num: number | bigint) { + const isInteger = typeof num === 'bigint' || Number.isInteger(num); + + if (!isInteger || num < 0 || num > BigInt('0xffffffffffffffff')) { + throw new Error('Input is not a 64-bit unsigned integer'); + } + + const encoding = new Uint8Array(8); + const view = new DataView(encoding.buffer); + view.setBigUint64(0, BigInt(num)); + + return encoding; +} + +/** + * decodeUint64 produces an integer from a binary representation. + * @param data - An typed array containing the big-endian encoding of an unsigned integer + * less than 2^64. This array must be at most 8 bytes long. + * @param decodingMode - Configure how the integer will be + * decoded. + * + * The options are: + * * "safe": The integer will be decoded as a Number, but if it is greater than + * Number.MAX_SAFE_INTEGER an error will be thrown. + * * "mixed": The integer will be decoded as a Number if it is less than or equal to + * Number.MAX_SAFE_INTEGER, otherwise it will be decoded as a BigInt. + * * "bigint": The integer will always be decoded as a BigInt. + * + * Defaults to "safe" if not included. + * @returns The integer that was encoded in the input data. The return type will + * be determined by the parameter decodingMode. + */ +export function decodeUint64(data: Uint8Array, decodingMode: 'safe'): number; +export function decodeUint64( + data: Uint8Array, + decodingMode: 'mixed' +): number | bigint; +export function decodeUint64(data: Uint8Array, decodingMode: 'bigint'): bigint; +export function decodeUint64(data: any, decodingMode: any = 'safe') { + if ( + decodingMode !== 'safe' && + decodingMode !== 'mixed' && + decodingMode !== 'bigint' + ) { + throw new Error(`Unknown decodingMode option: ${decodingMode}`); + } + + if (data.byteLength === 0 || data.byteLength > 8) { + throw new Error( + `Data has unacceptable length. Expected length is between 1 and 8, got ${data.byteLength}` + ); + } + + // insert 0s at the beginning if data is smaller than 8 bytes + const padding = new Uint8Array(8 - data.byteLength); + const encoding = concatArrays(padding, data); + const view = new DataView(encoding.buffer); + + const num = view.getBigUint64(0); + const isBig = num > BigInt(Number.MAX_SAFE_INTEGER); + + if (decodingMode === 'safe') { + if (isBig) { + throw new Error( + `Integer exceeds maximum safe integer: ${num.toString()}. Try decoding with "mixed" or "safe" decodingMode.` + ); + } + return Number(num); + } + + if (decodingMode === 'mixed' && !isBig) { + return Number(num); + } + + return num; +} diff --git a/src/group.ts b/src/group.ts new file mode 100644 index 0000000..0e26384 --- /dev/null +++ b/src/group.ts @@ -0,0 +1,98 @@ +import { Buffer } from 'buffer'; +import * as txnBuilder from './transaction'; +import * as nacl from './nacl/naclWrappers'; +import * as encoding from './encoding/encoding'; +import * as address from './encoding/address'; +import * as utils from './utils/utils'; + +const ALGORAND_MAX_TX_GROUP_SIZE = 16; + +interface EncodedTxGroup { + txlist: Buffer[]; +} + +/** + * Aux class for group id calculation of a group of transactions + */ +export class TxGroup { + name = 'Transaction group'; + tag = Buffer.from('TG'); + txGroupHashes: Buffer[]; + + constructor(hashes: Buffer[]) { + if (hashes.length > ALGORAND_MAX_TX_GROUP_SIZE) { + const errorMsg = `${hashes.length.toString()} transactions grouped together but max group size is ${ALGORAND_MAX_TX_GROUP_SIZE.toString()}`; + throw Error(errorMsg); + } + + this.txGroupHashes = hashes; + } + + // eslint-disable-next-line camelcase + get_obj_for_encoding() { + const txgroup: EncodedTxGroup = { + txlist: this.txGroupHashes, + }; + return txgroup; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(txgroupForEnc: EncodedTxGroup) { + const txn = Object.create(this.prototype); + txn.name = 'Transaction group'; + txn.tag = Buffer.from('TG'); + txn.txGroupHashes = []; + for (const hash of txgroupForEnc.txlist) { + txn.txGroupHashes.push(Buffer.from(hash)); + } + return txn; + } + + toByte() { + return encoding.encode(this.get_obj_for_encoding()); + } +} + +/** + * computeGroupID returns group ID for a group of transactions + * @param txns - array of transactions (every element is a dict or Transaction) + * @returns Buffer + */ +export function computeGroupID(txns: txnBuilder.TransactionLike[]) { + const hashes = []; + for (const txn of txns) { + const tx = txnBuilder.instantiateTxnIfNeeded(txn); + hashes.push(tx.rawTxID()); + } + + const txgroup = new TxGroup(hashes); + + const bytes = txgroup.toByte(); + const toBeHashed = Buffer.from(utils.concatArrays(txgroup.tag, bytes)); + const gid = nacl.genericHash(toBeHashed); + return Buffer.from(gid); +} + +/** + * assignGroupID assigns group id to a given list of unsigned transactions + * @param txns - array of transactions (every element is a dict or Transaction) + * @param from - optional sender address specifying which transaction return + * @returns possible list of matching transactions + */ +export function assignGroupID( + txns: txnBuilder.TransactionLike[], + from?: string +) { + const gid = computeGroupID(txns); + const result: txnBuilder.Transaction[] = []; + for (const txn of txns) { + const tx = txnBuilder.instantiateTxnIfNeeded(txn); + if (!from || address.encodeAddress(tx.from.publicKey) === from) { + tx.group = gid; + result.push(tx); + } + } + return result; +} + +export default TxGroup; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..8ed43c6 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,4 @@ +import * as algosdk from './main'; + +export * from './main'; +export default algosdk; diff --git a/src/logic/sourcemap.ts b/src/logic/sourcemap.ts new file mode 100644 index 0000000..b005649 --- /dev/null +++ b/src/logic/sourcemap.ts @@ -0,0 +1,67 @@ +import * as vlq from 'vlq'; + +export class SourceMap { + version: number; + sources: string[]; + names: string[]; + mappings: string; + + pcToLine: { [key: number]: number }; + lineToPc: { [key: number]: number[] }; + + constructor({ + version, + sources, + names, + mappings, + }: { + version: number; + sources: string[]; + names: string[]; + mappings: string; + }) { + this.version = version; + this.sources = sources; + this.names = names; + this.mappings = mappings; + + if (this.version !== 3) + throw new Error(`Only version 3 is supported, got ${this.version}`); + + if (this.mappings === undefined) + throw new Error( + 'mapping undefined, cannot build source map without `mapping`' + ); + + const pcList = this.mappings.split(';').map((m) => { + const decoded = vlq.decode(m); + if (decoded.length > 2) return decoded[2]; + return undefined; + }); + + this.pcToLine = {}; + this.lineToPc = {}; + + let lastLine = 0; + for (const [pc, lineDelta] of pcList.entries()) { + // If the delta is not undefined, the lastLine should be updated with + // lastLine + the delta + if (lineDelta !== undefined) { + lastLine += lineDelta; + } + + if (!(lastLine in this.lineToPc)) this.lineToPc[lastLine] = []; + + this.lineToPc[lastLine].push(pc); + this.pcToLine[pc] = lastLine; + } + } + + getLineForPc(pc: number): number | undefined { + return this.pcToLine[pc]; + } + + getPcsForLine(line: number): number[] | undefined { + return this.lineToPc[line]; + } +} diff --git a/src/logicsig.ts b/src/logicsig.ts new file mode 100644 index 0000000..34a35ca --- /dev/null +++ b/src/logicsig.ts @@ -0,0 +1,536 @@ +import { Buffer } from 'buffer'; +import * as nacl from './nacl/naclWrappers'; +import * as address from './encoding/address'; +import * as encoding from './encoding/encoding'; +import { verifyMultisig } from './multisig'; +import * as utils from './utils/utils'; +import * as txnBuilder from './transaction'; +import { isValidAddress } from './encoding/address'; +import { + EncodedLogicSig, + EncodedLogicSigAccount, + EncodedMultisig, + EncodedSignedTransaction, +} from './types/transactions/encoded'; +import { MultisigMetadata } from './types/multisig'; + +interface LogicSigStorageStructure { + logic: Uint8Array; + args: Uint8Array[]; + sig?: Uint8Array; + msig?: EncodedMultisig; +} + +/** sanityCheckProgram performs heuristic program validation: + * check if passed in bytes are Algorand address or is B64 encoded, rather than Teal bytes + * + * @param program - Program bytes to check + */ +export function sanityCheckProgram(program: Uint8Array) { + if (!program || program.length === 0) throw new Error('empty program'); + + const lineBreakOrd = '\n'.charCodeAt(0); + const blankSpaceOrd = ' '.charCodeAt(0); + const tildeOrd = '~'.charCodeAt(0); + + const isPrintable = (x: number) => blankSpaceOrd <= x && x <= tildeOrd; + const isAsciiPrintable = program.every( + (x: number) => x === lineBreakOrd || isPrintable(x) + ); + + if (isAsciiPrintable) { + const programStr = Buffer.from(program).toString(); + + if (isValidAddress(programStr)) + throw new Error('requesting program bytes, get Algorand address'); + + if (Buffer.from(programStr, 'base64').toString('base64') === programStr) + throw new Error('program should not be b64 encoded'); + + throw new Error( + 'program bytes are all ASCII printable characters, not looking like Teal byte code' + ); + } +} + +/** + LogicSig implementation + + LogicSig cannot sign transactions in all cases. Instead, use LogicSigAccount as a safe, general purpose signing mechanism. Since LogicSig does not track the provided signature's public key, LogicSig cannot sign transactions when delegated to a non-multisig account _and_ the sender is not the delegating account. + */ +export class LogicSig implements LogicSigStorageStructure { + tag = Buffer.from('Program'); + + logic: Uint8Array; + args: Uint8Array[]; + sig?: Uint8Array; + msig?: EncodedMultisig; + + constructor( + program: Uint8Array, + programArgs?: Array | null + ) { + if ( + programArgs && + (!Array.isArray(programArgs) || + !programArgs.every( + (arg) => arg.constructor === Uint8Array || Buffer.isBuffer(arg) + )) + ) { + throw new TypeError('Invalid arguments'); + } + + let args: Uint8Array[] | undefined; + if (programArgs != null) + args = programArgs.map((arg) => new Uint8Array(arg)); + + sanityCheckProgram(program); + + this.logic = program; + this.args = args; + this.sig = undefined; + this.msig = undefined; + } + + // eslint-disable-next-line camelcase + get_obj_for_encoding() { + const obj: EncodedLogicSig = { + l: this.logic, + }; + if (this.args) { + obj.arg = this.args; + } + if (this.sig) { + obj.sig = this.sig; + } else if (this.msig) { + obj.msig = this.msig; + } + return obj; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(encoded: EncodedLogicSig) { + const lsig = new LogicSig(encoded.l, encoded.arg); + lsig.sig = encoded.sig; + lsig.msig = encoded.msig; + return lsig; + } + + /** + * Performs signature verification + * @param publicKey - Verification key (derived from sender address or escrow address) + */ + verify(publicKey: Uint8Array) { + if (this.sig && this.msig) { + return false; + } + + try { + sanityCheckProgram(this.logic); + } catch (e) { + return false; + } + + const toBeSigned = utils.concatArrays(this.tag, this.logic); + + if (!this.sig && !this.msig) { + const hash = nacl.genericHash(toBeSigned); + return utils.arrayEqual(hash, publicKey); + } + + if (this.sig) { + return nacl.verify(toBeSigned, this.sig, publicKey); + } + + return verifyMultisig(toBeSigned, this.msig, publicKey); + } + + /** + * Compute hash of the logic sig program (that is the same as escrow account address) as string address + * @returns String representation of the address + */ + address() { + const toBeSigned = utils.concatArrays(this.tag, this.logic); + const hash = nacl.genericHash(toBeSigned); + return address.encodeAddress(new Uint8Array(hash)); + } + + /** + * Creates signature (if no msig provided) or multi signature otherwise + * @param secretKey - Secret key to sign with + * @param msig - Multisig account as \{version, threshold, addrs\} + */ + sign(secretKey: Uint8Array, msig?: MultisigMetadata) { + if (msig == null) { + this.sig = this.signProgram(secretKey); + } else { + const subsigs = msig.addrs.map((addr) => ({ + pk: address.decodeAddress(addr).publicKey, + })); + + this.msig = { + v: msig.version, + thr: msig.threshold, + subsig: subsigs, + }; + + const [sig, index] = this.singleSignMultisig(secretKey, this.msig); + this.msig.subsig[index].s = sig; + } + } + + /** + * Appends a signature to multi signature + * @param secretKey - Secret key to sign with + */ + appendToMultisig(secretKey: Uint8Array) { + if (this.msig === undefined) { + throw new Error('no multisig present'); + } + const [sig, index] = this.singleSignMultisig(secretKey, this.msig); + this.msig.subsig[index].s = sig; + } + + signProgram(secretKey: Uint8Array) { + const toBeSigned = utils.concatArrays(this.tag, this.logic); + const sig = nacl.sign(toBeSigned, secretKey); + return sig; + } + + singleSignMultisig( + secretKey: Uint8Array, + msig: EncodedMultisig + ): [sig: Uint8Array, index: number] { + let index = -1; + const myPk = nacl.keyPairFromSecretKey(secretKey).publicKey; + for (let i = 0; i < msig.subsig.length; i++) { + const { pk } = msig.subsig[i]; + if (utils.arrayEqual(pk, myPk)) { + index = i; + break; + } + } + if (index === -1) { + throw new Error('invalid secret key'); + } + const sig = this.signProgram(secretKey); + return [sig, index]; + } + + toByte() { + return encoding.encode(this.get_obj_for_encoding()); + } + + static fromByte(encoded: ArrayLike) { + const decodedObj = encoding.decode(encoded) as EncodedLogicSig; + return LogicSig.from_obj_for_encoding(decodedObj); + } +} + +/** + * Represents an account that can sign with a LogicSig program. + */ +export class LogicSigAccount { + lsig: LogicSig; + sigkey?: Uint8Array; + + /** + * Create a new LogicSigAccount. By default this will create an escrow + * LogicSig account. Call `sign` or `signMultisig` on the newly created + * LogicSigAccount to make it a delegated account. + * + * @param program - The compiled TEAL program which contains the logic for + * this LogicSig. + * @param args - An optional array of arguments for the program. + */ + constructor(program: Uint8Array, args?: Array | null) { + this.lsig = new LogicSig(program, args); + this.sigkey = undefined; + } + + // eslint-disable-next-line camelcase + get_obj_for_encoding() { + const obj: EncodedLogicSigAccount = { + lsig: this.lsig.get_obj_for_encoding(), + }; + if (this.sigkey) { + obj.sigkey = this.sigkey; + } + return obj; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(encoded: EncodedLogicSigAccount) { + const lsigAccount = new LogicSigAccount(encoded.lsig.l, encoded.lsig.arg); + lsigAccount.lsig = LogicSig.from_obj_for_encoding(encoded.lsig); + lsigAccount.sigkey = encoded.sigkey; + return lsigAccount; + } + + /** + * Encode this object into msgpack. + */ + toByte() { + return encoding.encode(this.get_obj_for_encoding()); + } + + /** + * Decode a msgpack object into a LogicSigAccount. + * @param encoded - The encoded LogicSigAccount. + */ + static fromByte(encoded: ArrayLike) { + const decodedObj = encoding.decode(encoded) as EncodedLogicSigAccount; + return LogicSigAccount.from_obj_for_encoding(decodedObj); + } + + /** + * Check if this LogicSigAccount has been delegated to another account with a + * signature. + * + * Note this function only checks for the presence of a delegation signature. + * To verify the delegation signature, use `verify`. + */ + isDelegated() { + return !!(this.lsig.sig || this.lsig.msig); + } + + /** + * Verifies this LogicSig's program and signatures. + * @returns true if and only if the LogicSig program and signatures are valid. + */ + verify() { + const addr = this.address(); + return this.lsig.verify(address.decodeAddress(addr).publicKey); + } + + /** + * Get the address of this LogicSigAccount. + * + * If the LogicSig is delegated to another account, this will return the + * address of that account. + * + * If the LogicSig is not delegated to another account, this will return an + * escrow address that is the hash of the LogicSig's program code. + */ + address() { + if (this.lsig.sig && this.lsig.msig) { + throw new Error( + 'LogicSig has too many signatures. At most one of sig or msig may be present' + ); + } + + if (this.lsig.sig) { + if (!this.sigkey) { + throw new Error('Signing key for delegated account is missing'); + } + return address.encodeAddress(this.sigkey); + } + + if (this.lsig.msig) { + const msigMetadata = { + version: this.lsig.msig.v, + threshold: this.lsig.msig.thr, + pks: this.lsig.msig.subsig.map((subsig) => subsig.pk), + }; + return address.encodeAddress(address.fromMultisigPreImg(msigMetadata)); + } + + return this.lsig.address(); + } + + /** + * Turns this LogicSigAccount into a delegated LogicSig. This type of LogicSig + * has the authority to sign transactions on behalf of another account, called + * the delegating account. Use this function if the delegating account is a + * multisig account. + * + * @param msig - The multisig delegating account + * @param secretKey - The secret key of one of the members of the delegating + * multisig account. Use `appendToMultisig` to add additional signatures + * from other members. + */ + signMultisig(msig: MultisigMetadata, secretKey: Uint8Array) { + this.lsig.sign(secretKey, msig); + } + + /** + * Adds an additional signature from a member of the delegating multisig + * account. + * + * @param secretKey - The secret key of one of the members of the delegating + * multisig account. + */ + appendToMultisig(secretKey: Uint8Array) { + this.lsig.appendToMultisig(secretKey); + } + + /** + * Turns this LogicSigAccount into a delegated LogicSig. This type of LogicSig + * has the authority to sign transactions on behalf of another account, called + * the delegating account. If the delegating account is a multisig account, + * use `signMultisig` instead. + * + * @param secretKey - The secret key of the delegating account. + */ + sign(secretKey: Uint8Array) { + this.lsig.sign(secretKey); + this.sigkey = nacl.keyPairFromSecretKey(secretKey).publicKey; + } +} + +function signLogicSigTransactionWithAddress( + txn: txnBuilder.Transaction, + lsig: LogicSig, + lsigAddress: Uint8Array +) { + if (!lsig.verify(lsigAddress)) { + throw new Error( + 'Logic signature verification failed. Ensure the program and signature are valid.' + ); + } + + const signedTxn: EncodedSignedTransaction = { + lsig: lsig.get_obj_for_encoding(), + txn: txn.get_obj_for_encoding(), + }; + + if (!nacl.bytesEqual(lsigAddress, txn.from.publicKey)) { + signedTxn.sgnr = Buffer.from(lsigAddress); + } + + return { + txID: txn.txID().toString(), + blob: encoding.encode(signedTxn), + }; +} + +/** + * signLogicSigTransactionObject takes a transaction and a LogicSig object and + * returns a signed transaction. + * + * @param txn - The transaction to sign. + * @param lsigObject - The LogicSig object that will sign the transaction. + * + * @returns Object containing txID and blob representing signed transaction. + */ +export function signLogicSigTransactionObject( + txn: txnBuilder.Transaction, + lsigObject: LogicSig | LogicSigAccount +) { + let lsig: LogicSig; + let lsigAddress: Uint8Array; + + if (lsigObject instanceof LogicSigAccount) { + lsig = lsigObject.lsig; + lsigAddress = address.decodeAddress(lsigObject.address()).publicKey; + } else { + lsig = lsigObject; + + if (lsig.sig) { + // For a LogicSig with a non-multisig delegating account, we cannot derive + // the address of that account from only its signature, so assume the + // delegating account is the sender. If that's not the case, the signing + // will fail. + lsigAddress = txn.from.publicKey; + } else if (lsig.msig) { + const msigMetadata = { + version: lsig.msig.v, + threshold: lsig.msig.thr, + pks: lsig.msig.subsig.map((subsig) => subsig.pk), + }; + lsigAddress = address.fromMultisigPreImg(msigMetadata); + } else { + lsigAddress = address.decodeAddress(lsig.address()).publicKey; + } + } + + return signLogicSigTransactionWithAddress(txn, lsig, lsigAddress); +} + +/** + * signLogicSigTransaction takes a transaction and a LogicSig object and returns + * a signed transaction. + * + * @param txn - The transaction to sign. + * @param lsigObject - The LogicSig object that will sign the transaction. + * + * @returns Object containing txID and blob representing signed transaction. + * @throws error on failure + */ +export function signLogicSigTransaction( + txn: txnBuilder.TransactionLike, + lsigObject: LogicSig | LogicSigAccount +) { + const algoTxn = txnBuilder.instantiateTxnIfNeeded(txn); + return signLogicSigTransactionObject(algoTxn, lsigObject); +} + +/** + * logicSigFromByte accepts encoded logic sig bytes and attempts to call logicsig.fromByte on it, + * returning the result + */ +export function logicSigFromByte(encoded: Uint8Array) { + return LogicSig.fromByte(encoded); +} + +const SIGN_PROGRAM_DATA_PREFIX = Buffer.from('ProgData'); + +/** + * tealSign creates a signature compatible with ed25519verify opcode from program hash + * @param sk - uint8array with secret key + * @param data - buffer with data to sign + * @param programHash - string representation of teal program hash (= contract address for LogicSigs) + */ +export function tealSign( + sk: Uint8Array, + data: Uint8Array | Buffer, + programHash: string +) { + const parts = utils.concatArrays( + address.decodeAddress(programHash).publicKey, + data + ); + const toBeSigned = Buffer.from( + utils.concatArrays(SIGN_PROGRAM_DATA_PREFIX, parts) + ); + return nacl.sign(toBeSigned, sk); +} + +/** + * verifyTealSign verifies a signature as would the ed25519verify opcode + * @param data - buffer with original signed data + * @param programHash - string representation of teal program hash (= contract address for LogicSigs) + * @param sig - uint8array with the signature to verify (produced by tealSign/tealSignFromProgram) + * @param pk - uint8array with public key to verify against + */ +export function verifyTealSign( + data: Uint8Array | Buffer, + programHash: string, + sig: Uint8Array, + pk: Uint8Array +) { + const parts = utils.concatArrays( + address.decodeAddress(programHash).publicKey, + data + ); + const toBeSigned = Buffer.from( + utils.concatArrays(SIGN_PROGRAM_DATA_PREFIX, parts) + ); + return nacl.verify(toBeSigned, sig, pk); +} + +/** + * tealSignFromProgram creates a signature compatible with ed25519verify opcode from raw program bytes + * @param sk - uint8array with secret key + * @param data - buffer with data to sign + * @param program - buffer with teal program + */ +export function tealSignFromProgram( + sk: Uint8Array, + data: Uint8Array | Buffer, + program: Uint8Array +) { + const lsig = new LogicSig(program); + const contractAddress = lsig.address(); + return tealSign(sk, data, contractAddress); +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..aadb490 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,190 @@ +import { Buffer } from 'buffer'; +import * as nacl from './nacl/naclWrappers'; +import * as address from './encoding/address'; +import * as encoding from './encoding/encoding'; +import * as txnBuilder from './transaction'; +import Bid, { BidOptions } from './bid'; +import * as convert from './convert'; +import * as utils from './utils/utils'; + +const SIGN_BYTES_PREFIX = Buffer.from([77, 88]); // "MX" + +// Errors +export const MULTISIG_BAD_SENDER_ERROR_MSG = + 'The transaction sender address and multisig preimage do not match.'; + +/** + * signTransaction takes an object with either payment or key registration fields and + * a secret key and returns a signed blob. + * + * Payment transaction fields: from, to, amount, fee, firstRound, lastRound, genesisHash, + * note(optional), GenesisID(optional), closeRemainderTo(optional) + * + * Key registration fields: fee, firstRound, lastRound, voteKey, selectionKey, voteFirst, + * voteLast, voteKeyDilution, genesisHash, note(optional), GenesisID(optional) + * + * If flatFee is not set and the final calculated fee is lower than the protocol minimum fee, the fee will be increased to match the minimum. + * @param txn - object with either payment or key registration fields + * @param sk - Algorand Secret Key + * @returns object contains the binary signed transaction and its txID + */ +export function signTransaction( + txn: txnBuilder.TransactionLike, + sk: Uint8Array +) { + if (typeof txn.from === 'undefined') { + // Get pk from sk if no sender specified + const key = nacl.keyPairFromSecretKey(sk); + // eslint-disable-next-line no-param-reassign + txn.from = address.encodeAddress(key.publicKey); + } + const algoTxn = txnBuilder.instantiateTxnIfNeeded(txn); + + return { + txID: algoTxn.txID().toString(), + blob: algoTxn.signTxn(sk), + }; +} + +/** + * signBid takes an object with the following fields: bidder key, bid amount, max price, bid ID, auctionKey, auction ID, + * and a secret key and returns a signed blob to be inserted into a transaction Algorand note field. + * @param bid - Algorand Bid + * @param sk - Algorand secret key + * @returns Uint8Array binary signed bid + */ +export function signBid(bid: BidOptions, sk: Uint8Array) { + const signedBid = new Bid(bid); + return signedBid.signBid(sk); +} + +/** + * signBytes takes arbitrary bytes and a secret key, prepends the bytes with "MX" for domain separation, signs the bytes + * with the private key, and returns the signature. + * @param bytes - Uint8array + * @param sk - Algorand secret key + * @returns binary signature + */ +export function signBytes(bytes: Uint8Array, sk: Uint8Array) { + const toBeSigned = Buffer.from(utils.concatArrays(SIGN_BYTES_PREFIX, bytes)); + const sig = nacl.sign(toBeSigned, sk); + return sig; +} + +/** + * verifyBytes takes array of bytes, an address, and a signature and verifies if the signature is correct for the public + * key and the bytes (the bytes should have been signed with "MX" prepended for domain separation). + * @param bytes - Uint8Array + * @param signature - binary signature + * @param addr - string address + * @returns bool + */ +export function verifyBytes( + bytes: Uint8Array, + signature: Uint8Array, + addr: string +) { + const toBeVerified = Buffer.from( + utils.concatArrays(SIGN_BYTES_PREFIX, bytes) + ); + const pk = address.decodeAddress(addr).publicKey; + return nacl.verify(toBeVerified, signature, pk); +} + +/** + * encodeObj takes a javascript object and returns its msgpack encoding + * Note that the encoding sorts the fields alphabetically + * @param o - js obj + * @returns Uint8Array binary representation + */ +export function encodeObj(o: Record) { + return new Uint8Array(encoding.encode(o)); +} + +/** + * decodeObj takes a Uint8Array and returns its javascript obj + * @param o - Uint8Array to decode + * @returns object + */ +export function decodeObj(o: ArrayLike) { + return encoding.decode(o); +} + +export const ERROR_MULTISIG_BAD_SENDER = new Error( + MULTISIG_BAD_SENDER_ERROR_MSG +); +export const ERROR_INVALID_MICROALGOS = new Error( + convert.INVALID_MICROALGOS_ERROR_MSG +); + +export { default as Algodv2 } from './client/v2/algod/algod'; +export { default as Kmd } from './client/kmd'; +export { default as IntDecoding } from './types/intDecoding'; +export { default as Account } from './types/account'; +export { default as Indexer } from './client/v2/indexer/indexer'; +export { + BaseHTTPClient, + BaseHTTPClientResponse, + BaseHTTPClientError, +} from './client/baseHTTPClient'; +export { + AlgodTokenHeader, + IndexerTokenHeader, + KMDTokenHeader, + CustomTokenHeader, + TokenHeader, +} from './client/urlTokenBaseHTTPClient'; +export { waitForConfirmation } from './wait'; +export { + isValidAddress, + encodeAddress, + decodeAddress, + getApplicationAddress, +} from './encoding/address'; +export { bytesToBigInt, bigIntToBytes } from './encoding/bigint'; +export { encodeUint64, decodeUint64 } from './encoding/uint64'; +export { default as generateAccount } from './account'; +export * as modelsv2 from './client/v2/algod/models/types'; +export * as indexerModels from './client/v2/indexer/models/types'; +export { + mnemonicToMasterDerivationKey, + masterDerivationKeyToMnemonic, + secretKeyToMnemonic, + mnemonicToSecretKey, + seedFromMnemonic, + mnemonicFromSeed, +} from './mnemonic/mnemonic'; +export { + microalgosToAlgos, + algosToMicroalgos, + INVALID_MICROALGOS_ERROR_MSG, +} from './convert'; +export { computeGroupID, assignGroupID } from './group'; +export { + LogicSig, + LogicSigAccount, + signLogicSigTransaction, + signLogicSigTransactionObject, + logicSigFromByte, + tealSign, + tealSignFromProgram, + verifyTealSign, +} from './logicsig'; +export { + signMultisigTransaction, + mergeMultisigTransactions, + appendSignMultisigTransaction, + createMultisigTransaction, + appendSignRawMultisigSignature, + verifyMultisig, + multisigAddress, +} from './multisig'; +export { SourceMap } from './logic/sourcemap'; + +export * from './dryrun'; +export * from './makeTxn'; +export * from './transaction'; +export * from './signer'; +export * from './composer'; +export * from './types'; +export * from './abi'; diff --git a/src/makeTxn.ts b/src/makeTxn.ts new file mode 100644 index 0000000..9814989 --- /dev/null +++ b/src/makeTxn.ts @@ -0,0 +1,1448 @@ +import * as txnBuilder from './transaction'; +import { OnApplicationComplete } from './types/transactions/base'; +import { + // Transaction types + PaymentTxn, + KeyRegistrationTxn, + + // Utilities + TransactionType, + MustHaveSuggestedParams, + AssetCreateTxn, + AssetConfigTxn, + AssetDestroyTxn, + AssetFreezeTxn, + AssetTransferTxn, + AppCreateTxn, + AppUpdateTxn, + AppDeleteTxn, + AppOptInTxn, + AppCloseOutTxn, + AppClearStateTxn, + AppNoOpTxn, +} from './types/transactions'; +import { RenameProperties, RenameProperty, Expand } from './types/utils'; + +/** + * makePaymentTxnWithSuggestedParams takes payment arguments and returns a Transaction object + * @param from - string representation of Algorand address of sender + * @param to - string representation of Algorand address of recipient + * @param amount - integer amount to send, in microAlgos + * @param closeRemainderTo - optionally close out remaining account balance to this account, represented as string rep of Algorand address + * @param note - uint8array of arbitrary data for sender to store + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param rekeyTo - rekeyTo address, optional + */ +export function makePaymentTxnWithSuggestedParams( + from: PaymentTxn['from'], + to: PaymentTxn['to'], + amount: PaymentTxn['amount'], + closeRemainderTo: PaymentTxn['closeRemainderTo'], + note: PaymentTxn['note'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + rekeyTo?: PaymentTxn['reKeyTo'] +) { + const o: PaymentTxn = { + from, + to, + amount, + closeRemainderTo, + note, + suggestedParams, + type: TransactionType.pay, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makePaymentTxnWithSuggestedParams, instead accepting an arguments object +export function makePaymentTxnWithSuggestedParamsFromObject( + o: Expand< + Pick< + RenameProperty, 'reKeyTo', 'rekeyTo'>, + | 'from' + | 'to' + | 'amount' + | 'closeRemainderTo' + | 'note' + | 'suggestedParams' + | 'rekeyTo' + > + > +) { + return makePaymentTxnWithSuggestedParams( + o.from, + o.to, + o.amount, + o.closeRemainderTo, + o.note, + o.suggestedParams, + o.rekeyTo + ); +} + +/** + * makeKeyRegistrationTxnWithSuggestedParams takes key registration arguments and returns a Transaction object for + * that key registration operation + * + * @param from - string representation of Algorand address of sender + * @param note - uint8array of arbitrary data for sender to store + * @param voteKey - voting key. for key deregistration, leave undefined + * @param selectionKey - selection key. for key deregistration, leave undefined + * @param voteFirst - first round on which voteKey is valid + * @param voteLast - last round on which voteKey is valid + * @param voteKeyDilution - integer + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param rekeyTo - rekeyTo address, optional + * @param nonParticipation - configure whether the address wants to stop participating. If true, + * voteKey, selectionKey, voteFirst, voteLast, and voteKeyDilution must be undefined. + * @param stateProofKey - state proof key. for key deregistration, leave undefined + */ +export function makeKeyRegistrationTxnWithSuggestedParams( + from: KeyRegistrationTxn['from'], + note: KeyRegistrationTxn['note'], + voteKey: KeyRegistrationTxn['voteKey'], + selectionKey: KeyRegistrationTxn['selectionKey'], + voteFirst: KeyRegistrationTxn['voteFirst'], + voteLast: KeyRegistrationTxn['voteLast'], + voteKeyDilution: KeyRegistrationTxn['voteKeyDilution'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + rekeyTo?: KeyRegistrationTxn['reKeyTo'], + nonParticipation?: false, + stateProofKey?: KeyRegistrationTxn['stateProofKey'] +): txnBuilder.Transaction; +export function makeKeyRegistrationTxnWithSuggestedParams( + from: KeyRegistrationTxn['from'], + note: KeyRegistrationTxn['note'], + voteKey: undefined, + selectionKey: undefined, + voteFirst: undefined, + voteLast: undefined, + voteKeyDilution: undefined, + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + rekeyTo?: KeyRegistrationTxn['reKeyTo'], + nonParticipation?: true, + stateProofKey?: undefined +): txnBuilder.Transaction; +export function makeKeyRegistrationTxnWithSuggestedParams( + from: any, + note: any, + voteKey: any, + selectionKey: any, + voteFirst: any, + voteLast: any, + voteKeyDilution: any, + suggestedParams: any, + rekeyTo?: any, + nonParticipation = false, + stateProofKey: any = undefined +) { + const o: KeyRegistrationTxn = { + from, + note, + voteKey, + selectionKey, + voteFirst, + voteLast, + voteKeyDilution, + suggestedParams, + type: TransactionType.keyreg, + reKeyTo: rekeyTo, + nonParticipation, + stateProofKey, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeKeyRegistrationTxnWithSuggestedParams, instead accepting an arguments object +export function makeKeyRegistrationTxnWithSuggestedParamsFromObject( + o: Expand< + Pick< + RenameProperty< + MustHaveSuggestedParams, + 'reKeyTo', + 'rekeyTo' + >, + | 'from' + | 'note' + | 'voteKey' + | 'selectionKey' + | 'stateProofKey' + | 'voteFirst' + | 'voteLast' + | 'voteKeyDilution' + | 'suggestedParams' + | 'rekeyTo' + > & { + nonParticipation?: false; + } + > +): txnBuilder.Transaction; +export function makeKeyRegistrationTxnWithSuggestedParamsFromObject( + o: Expand< + Pick< + RenameProperty< + MustHaveSuggestedParams, + 'reKeyTo', + 'rekeyTo' + >, + 'from' | 'note' | 'suggestedParams' | 'rekeyTo' | 'nonParticipation' + > + > +): txnBuilder.Transaction; +export function makeKeyRegistrationTxnWithSuggestedParamsFromObject(o: any) { + return makeKeyRegistrationTxnWithSuggestedParams( + o.from, + o.note, + o.voteKey, + o.selectionKey, + o.voteFirst, + o.voteLast, + o.voteKeyDilution, + o.suggestedParams, + o.rekeyTo, + o.nonParticipation, + o.stateProofKey + ); +} + +/** makeAssetCreateTxnWithSuggestedParams takes asset creation arguments and returns a Transaction object + * for creating that asset + * + * @param from - string representation of Algorand address of sender + * @param note - uint8array of arbitrary data for sender to store + * @param total - integer total supply of the asset + * @param decimals - integer number of decimals for asset unit calculation + * @param defaultFrozen - boolean whether asset accounts should default to being frozen + * @param manager - string representation of Algorand address in charge of reserve, freeze, clawback, destruction, etc + * @param reserve - string representation of Algorand address representing asset reserve + * @param freeze - string representation of Algorand address with power to freeze/unfreeze asset holdings + * @param clawback - string representation of Algorand address with power to revoke asset holdings + * @param unitName - string units name for this asset + * @param assetName - string name for this asset + * @param assetURL - string URL relating to this asset + * @param assetMetadataHash - Uint8Array or UTF-8 string representation of a hash commitment with respect to the asset. Must be exactly 32 bytes long. + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param rekeyTo - rekeyTo address, optional + */ +export function makeAssetCreateTxnWithSuggestedParams( + from: AssetCreateTxn['from'], + note: AssetCreateTxn['note'], + total: AssetCreateTxn['assetTotal'], + decimals: AssetCreateTxn['assetDecimals'], + defaultFrozen: AssetCreateTxn['assetDefaultFrozen'], + manager: AssetCreateTxn['assetManager'], + reserve: AssetCreateTxn['assetReserve'], + freeze: AssetCreateTxn['assetFreeze'], + clawback: AssetCreateTxn['assetClawback'], + unitName: AssetCreateTxn['assetUnitName'], + assetName: AssetCreateTxn['assetName'], + assetURL: AssetCreateTxn['assetURL'], + assetMetadataHash: AssetCreateTxn['assetMetadataHash'] | undefined, + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + rekeyTo?: AssetCreateTxn['reKeyTo'] +) { + const o: AssetCreateTxn = { + from, + note, + suggestedParams, + assetTotal: total, + assetDecimals: decimals, + assetDefaultFrozen: defaultFrozen, + assetUnitName: unitName, + assetName, + assetURL, + assetMetadataHash, + assetManager: manager, + assetReserve: reserve, + assetFreeze: freeze, + assetClawback: clawback, + type: TransactionType.acfg, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeAssetCreateTxnWithSuggestedParams, instead accepting an arguments object +export function makeAssetCreateTxnWithSuggestedParamsFromObject( + o: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + reKeyTo: 'rekeyTo'; + assetTotal: 'total'; + assetDecimals: 'decimals'; + assetDefaultFrozen: 'defaultFrozen'; + assetManager: 'manager'; + assetReserve: 'reserve'; + assetFreeze: 'freeze'; + assetClawback: 'clawback'; + assetUnitName: 'unitName'; + } + >, + | 'from' + | 'note' + | 'total' + | 'decimals' + | 'defaultFrozen' + | 'manager' + | 'reserve' + | 'freeze' + | 'clawback' + | 'unitName' + | 'assetName' + | 'assetURL' + | 'assetMetadataHash' + | 'suggestedParams' + | 'rekeyTo' + > + > +) { + return makeAssetCreateTxnWithSuggestedParams( + o.from, + o.note, + o.total, + o.decimals, + o.defaultFrozen, + o.manager, + o.reserve, + o.freeze, + o.clawback, + o.unitName, + o.assetName, + o.assetURL, + o.assetMetadataHash, + o.suggestedParams, + o.rekeyTo + ); +} + +/** makeAssetConfigTxnWithSuggestedParams can be issued by the asset manager to change the manager, reserve, freeze, or clawback + * you must respecify existing addresses to keep them the same; leaving a field blank is the same as turning + * that feature off for this asset + * + * @param from - string representation of Algorand address of sender + * @param note - uint8array of arbitrary data for sender to store + * @param assetIndex - int asset index uniquely specifying the asset + * @param manager - string representation of new asset manager Algorand address + * @param reserve - string representation of new reserve Algorand address + * @param freeze - string representation of new freeze manager Algorand address + * @param clawback - string representation of new revocation manager Algorand address + * @param strictEmptyAddressChecking - boolean - throw an error if any of manager, reserve, freeze, or clawback are undefined. optional, defaults to true. + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param rekeyTo - rekeyTo address, optional + */ +export function makeAssetConfigTxnWithSuggestedParams( + from: AssetConfigTxn['from'], + note: AssetConfigTxn['note'], + assetIndex: AssetConfigTxn['assetIndex'], + manager: AssetConfigTxn['assetManager'], + reserve: AssetConfigTxn['assetReserve'], + freeze: AssetConfigTxn['assetFreeze'], + clawback: AssetConfigTxn['assetClawback'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + strictEmptyAddressChecking = true, + rekeyTo?: AssetConfigTxn['reKeyTo'] +) { + if ( + strictEmptyAddressChecking && + (manager === undefined || + reserve === undefined || + freeze === undefined || + clawback === undefined) + ) { + throw Error( + 'strict empty address checking was turned on, but at least one empty address was provided' + ); + } + const o: AssetConfigTxn = { + from, + suggestedParams, + assetIndex, + assetManager: manager, + assetReserve: reserve, + assetFreeze: freeze, + assetClawback: clawback, + type: TransactionType.acfg, + note, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeAssetConfigTxnWithSuggestedParams, instead accepting an arguments object +export function makeAssetConfigTxnWithSuggestedParamsFromObject( + o: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + reKeyTo: 'rekeyTo'; + assetManager: 'manager'; + assetReserve: 'reserve'; + assetFreeze: 'freeze'; + assetClawback: 'clawback'; + } + >, + | 'from' + | 'note' + | 'assetIndex' + | 'manager' + | 'reserve' + | 'freeze' + | 'clawback' + | 'suggestedParams' + | 'rekeyTo' + > & { + strictEmptyAddressChecking: boolean; + } + > +) { + return makeAssetConfigTxnWithSuggestedParams( + o.from, + o.note, + o.assetIndex, + o.manager, + o.reserve, + o.freeze, + o.clawback, + o.suggestedParams, + o.strictEmptyAddressChecking, + o.rekeyTo + ); +} + +/** makeAssetDestroyTxnWithSuggestedParams will allow the asset's manager to remove this asset from the ledger, so long + * as all outstanding assets are held by the creator. + * + * @param from - string representation of Algorand address of sender + * @param note - uint8array of arbitrary data for sender to store + * @param assetIndex - int asset index uniquely specifying the asset + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param rekeyTo - rekeyTo address, optional + */ +export function makeAssetDestroyTxnWithSuggestedParams( + from: AssetDestroyTxn['from'], + note: AssetDestroyTxn['note'], + assetIndex: AssetDestroyTxn['assetIndex'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + rekeyTo?: AssetDestroyTxn['reKeyTo'] +) { + const o: AssetDestroyTxn = { + from, + suggestedParams, + assetIndex, + type: TransactionType.acfg, + note, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeAssetDestroyTxnWithSuggestedParams, instead accepting an arguments object +export function makeAssetDestroyTxnWithSuggestedParamsFromObject( + o: Expand< + Pick< + RenameProperty< + MustHaveSuggestedParams, + 'reKeyTo', + 'rekeyTo' + >, + 'from' | 'note' | 'assetIndex' | 'suggestedParams' | 'rekeyTo' + > + > +) { + return makeAssetDestroyTxnWithSuggestedParams( + o.from, + o.note, + o.assetIndex, + o.suggestedParams, + o.rekeyTo + ); +} + +/** makeAssetFreezeTxnWithSuggestedParams will allow the asset's freeze manager to freeze or un-freeze an account, + * blocking or allowing asset transfers to and from the targeted account. + * + * @param from - string representation of Algorand address of sender + * @param note - uint8array of arbitrary data for sender to store + * @param assetIndex - int asset index uniquely specifying the asset + * @param freezeTarget - string representation of Algorand address being frozen or unfrozen + * @param freezeState - true if freezeTarget should be frozen, false if freezeTarget should be allowed to transact + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param rekeyTo - rekeyTo address, optional + */ +export function makeAssetFreezeTxnWithSuggestedParams( + from: AssetFreezeTxn['from'], + note: AssetFreezeTxn['note'], + assetIndex: AssetFreezeTxn['assetIndex'], + freezeTarget: AssetFreezeTxn['freezeAccount'], + freezeState: AssetFreezeTxn['freezeState'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + rekeyTo?: AssetFreezeTxn['reKeyTo'] +) { + const o: AssetFreezeTxn = { + from, + type: TransactionType.afrz, + freezeAccount: freezeTarget, + assetIndex, + freezeState, + note, + suggestedParams, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeAssetFreezeTxnWithSuggestedParams, instead accepting an arguments object +export function makeAssetFreezeTxnWithSuggestedParamsFromObject( + o: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + freezeAccount: 'freezeTarget'; + reKeyTo: 'rekeyTo'; + } + >, + | 'from' + | 'note' + | 'assetIndex' + | 'freezeTarget' + | 'freezeState' + | 'suggestedParams' + | 'rekeyTo' + > + > +) { + return makeAssetFreezeTxnWithSuggestedParams( + o.from, + o.note, + o.assetIndex, + o.freezeTarget, + o.freezeState, + o.suggestedParams, + o.rekeyTo + ); +} + +/** makeAssetTransferTxnWithSuggestedParams allows for the creation of an asset transfer transaction. + * Special case: to begin accepting assets, set amount=0 and from=to. + * + * @param from - string representation of Algorand address of sender + * @param to - string representation of Algorand address of asset recipient + * @param closeRemainderTo - optional - string representation of Algorand address - if provided, + * send all remaining assets after transfer to the "closeRemainderTo" address and close "from"'s asset holdings + * @param revocationTarget - optional - string representation of Algorand address - if provided, + * and if "from" is the asset's revocation manager, then deduct from "revocationTarget" rather than "from" + * @param amount - integer amount of assets to send + * @param note - uint8array of arbitrary data for sender to store + * @param assetIndex - int asset index uniquely specifying the asset + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param rekeyTo - rekeyTo address, optional + */ +export function makeAssetTransferTxnWithSuggestedParams( + from: AssetTransferTxn['from'], + to: AssetTransferTxn['to'], + closeRemainderTo: AssetTransferTxn['closeRemainderTo'], + revocationTarget: AssetTransferTxn['assetRevocationTarget'], + amount: AssetTransferTxn['amount'], + note: AssetTransferTxn['note'], + assetIndex: AssetTransferTxn['assetIndex'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + rekeyTo?: AssetTransferTxn['reKeyTo'] +) { + const o: AssetTransferTxn = { + type: TransactionType.axfer, + from, + to, + amount, + suggestedParams, + assetIndex, + note, + assetRevocationTarget: revocationTarget, + closeRemainderTo, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeAssetTransferTxnWithSuggestedParams, instead accepting an arguments object +export function makeAssetTransferTxnWithSuggestedParamsFromObject( + o: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + assetRevocationTarget: 'revocationTarget'; + reKeyTo: 'rekeyTo'; + } + >, + | 'from' + | 'to' + | 'closeRemainderTo' + | 'revocationTarget' + | 'amount' + | 'note' + | 'assetIndex' + | 'suggestedParams' + | 'rekeyTo' + > + > +) { + return makeAssetTransferTxnWithSuggestedParams( + o.from, + o.to, + o.closeRemainderTo, + o.revocationTarget, + o.amount, + o.note, + o.assetIndex, + o.suggestedParams, + o.rekeyTo + ); +} + +/** + * Make a transaction that will create an application. + * @param from - address of sender + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param onComplete - algosdk.OnApplicationComplete, what application should do once the program is done being run + * @param approvalProgram - Uint8Array, the compiled TEAL that approves a transaction + * @param clearProgram - Uint8Array, the compiled TEAL that runs when clearing state + * @param numLocalInts - restricts number of ints in per-user local state + * @param numLocalByteSlices - restricts number of byte slices in per-user local state + * @param numGlobalInts - restricts number of ints in global state + * @param numGlobalByteSlices - restricts number of byte slices in global state + * @param appArgs - Array of Uint8Array, any additional arguments to the application + * @param accounts - Array of Address strings, any additional accounts to supply to the application + * @param foreignApps - Array of int, any other apps used by the application, identified by index + * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param note - Arbitrary data for sender to store + * @param lease - Lease a transaction + * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions + * @param extraPages - integer extra pages of memory to rent on creation of application + * @param boxes - Array of BoxReference, app ID and name of box to be accessed + */ +export function makeApplicationCreateTxn( + from: AppCreateTxn['from'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + onComplete: AppCreateTxn['appOnComplete'], + approvalProgram: AppCreateTxn['appApprovalProgram'], + clearProgram: AppCreateTxn['appClearProgram'], + numLocalInts: AppCreateTxn['appLocalInts'], + numLocalByteSlices: AppCreateTxn['appLocalByteSlices'], + numGlobalInts: AppCreateTxn['appGlobalInts'], + numGlobalByteSlices: AppCreateTxn['appGlobalByteSlices'], + appArgs?: AppCreateTxn['appArgs'], + accounts?: AppCreateTxn['appAccounts'], + foreignApps?: AppCreateTxn['appForeignApps'], + foreignAssets?: AppCreateTxn['appForeignAssets'], + note?: AppCreateTxn['note'], + lease?: AppCreateTxn['lease'], + rekeyTo?: AppCreateTxn['reKeyTo'], + extraPages?: AppCreateTxn['extraPages'], + boxes?: AppCreateTxn['boxes'] +) { + const o: AppCreateTxn = { + type: TransactionType.appl, + from, + suggestedParams, + appIndex: 0, + appOnComplete: onComplete, + appLocalInts: numLocalInts, + appLocalByteSlices: numLocalByteSlices, + appGlobalInts: numGlobalInts, + appGlobalByteSlices: numGlobalByteSlices, + appApprovalProgram: approvalProgram, + appClearProgram: clearProgram, + appArgs, + appAccounts: accounts, + appForeignApps: foreignApps, + appForeignAssets: foreignAssets, + boxes, + note, + lease, + reKeyTo: rekeyTo, + extraPages, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeApplicationCreateTxn, instead accepting an arguments object +export function makeApplicationCreateTxnFromObject( + o: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + appOnComplete: 'onComplete'; + appApprovalProgram: 'approvalProgram'; + appClearProgram: 'clearProgram'; + appLocalInts: 'numLocalInts'; + appLocalByteSlices: 'numLocalByteSlices'; + appGlobalInts: 'numGlobalInts'; + appGlobalByteSlices: 'numGlobalByteSlices'; + appAccounts: 'accounts'; + appForeignApps: 'foreignApps'; + appForeignAssets: 'foreignAssets'; + reKeyTo: 'rekeyTo'; + } + >, + | 'from' + | 'suggestedParams' + | 'onComplete' + | 'approvalProgram' + | 'clearProgram' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'appArgs' + | 'accounts' + | 'foreignApps' + | 'foreignAssets' + | 'boxes' + | 'note' + | 'lease' + | 'rekeyTo' + | 'extraPages' + > + > +) { + return makeApplicationCreateTxn( + o.from, + o.suggestedParams, + o.onComplete, + o.approvalProgram, + o.clearProgram, + o.numLocalInts, + o.numLocalByteSlices, + o.numGlobalInts, + o.numGlobalByteSlices, + o.appArgs, + o.accounts, + o.foreignApps, + o.foreignAssets, + o.note, + o.lease, + o.rekeyTo, + o.extraPages, + o.boxes + ); +} + +/** + * Make a transaction that changes an application's approval and clear programs + * @param from - address of sender + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param appIndex - the ID of the app to be updated + * @param approvalProgram - Uint8Array, the compiled TEAL that approves a transaction + * @param clearProgram - Uint8Array, the compiled TEAL that runs when clearing state + * @param appArgs - Array of Uint8Array, any additional arguments to the application + * @param accounts - Array of Address strings, any additional accounts to supply to the application + * @param foreignApps - Array of int, any other apps used by the application, identified by index + * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param note - Arbitrary data for sender to store + * @param lease - Lease a transaction + * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions + * @param boxes - Array of BoxReference, app ID and name of box to be accessed + */ +export function makeApplicationUpdateTxn( + from: AppUpdateTxn['from'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + appIndex: AppUpdateTxn['appIndex'], + approvalProgram: AppUpdateTxn['appApprovalProgram'], + clearProgram: AppUpdateTxn['appClearProgram'], + appArgs?: AppUpdateTxn['appArgs'], + accounts?: AppUpdateTxn['appAccounts'], + foreignApps?: AppUpdateTxn['appForeignApps'], + foreignAssets?: AppUpdateTxn['appForeignAssets'], + note?: AppUpdateTxn['note'], + lease?: AppUpdateTxn['lease'], + rekeyTo?: AppUpdateTxn['reKeyTo'], + boxes?: AppUpdateTxn['boxes'] +) { + const o: AppUpdateTxn = { + type: TransactionType.appl, + from, + suggestedParams, + appIndex, + appApprovalProgram: approvalProgram, + appOnComplete: OnApplicationComplete.UpdateApplicationOC, + appClearProgram: clearProgram, + appArgs, + appAccounts: accounts, + appForeignApps: foreignApps, + appForeignAssets: foreignAssets, + boxes, + note, + lease, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeApplicationUpdateTxn, instead accepting an arguments object +export function makeApplicationUpdateTxnFromObject( + o: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + appApprovalProgram: 'approvalProgram'; + appClearProgram: 'clearProgram'; + appAccounts: 'accounts'; + appForeignApps: 'foreignApps'; + appForeignAssets: 'foreignAssets'; + reKeyTo: 'rekeyTo'; + } + >, + | 'from' + | 'suggestedParams' + | 'appIndex' + | 'approvalProgram' + | 'clearProgram' + | 'appArgs' + | 'accounts' + | 'foreignApps' + | 'foreignAssets' + | 'boxes' + | 'note' + | 'lease' + | 'rekeyTo' + > + > +) { + return makeApplicationUpdateTxn( + o.from, + o.suggestedParams, + o.appIndex, + o.approvalProgram, + o.clearProgram, + o.appArgs, + o.accounts, + o.foreignApps, + o.foreignAssets, + o.note, + o.lease, + o.rekeyTo, + o.boxes + ); +} + +/** + * Make a transaction that deletes an application + * @param from - address of sender + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param appIndex - the ID of the app to be deleted + * @param appArgs - Array of Uint8Array, any additional arguments to the application + * @param accounts - Array of Address strings, any additional accounts to supply to the application + * @param foreignApps - Array of int, any other apps used by the application, identified by index + * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param note - Arbitrary data for sender to store + * @param lease - Lease a transaction + * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions + * @param boxes - Array of BoxReference, app ID and name of box to be accessed + */ +export function makeApplicationDeleteTxn( + from: AppDeleteTxn['from'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + appIndex: AppDeleteTxn['appIndex'], + appArgs?: AppDeleteTxn['appArgs'], + accounts?: AppDeleteTxn['appAccounts'], + foreignApps?: AppDeleteTxn['appForeignApps'], + foreignAssets?: AppDeleteTxn['appForeignAssets'], + note?: AppDeleteTxn['note'], + lease?: AppDeleteTxn['lease'], + rekeyTo?: AppDeleteTxn['reKeyTo'], + boxes?: AppDeleteTxn['boxes'] +) { + const o: AppDeleteTxn = { + type: TransactionType.appl, + from, + suggestedParams, + appIndex, + appOnComplete: OnApplicationComplete.DeleteApplicationOC, + appArgs, + appAccounts: accounts, + appForeignApps: foreignApps, + appForeignAssets: foreignAssets, + boxes, + note, + lease, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeApplicationDeleteTxn, instead accepting an arguments object +export function makeApplicationDeleteTxnFromObject( + o: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + appAccounts: 'accounts'; + appForeignApps: 'foreignApps'; + appForeignAssets: 'foreignAssets'; + reKeyTo: 'rekeyTo'; + } + >, + | 'from' + | 'suggestedParams' + | 'appIndex' + | 'appArgs' + | 'accounts' + | 'foreignApps' + | 'foreignAssets' + | 'boxes' + | 'note' + | 'lease' + | 'rekeyTo' + > + > +) { + return makeApplicationDeleteTxn( + o.from, + o.suggestedParams, + o.appIndex, + o.appArgs, + o.accounts, + o.foreignApps, + o.foreignAssets, + o.note, + o.lease, + o.rekeyTo, + o.boxes + ); +} + +/** + * Make a transaction that opts in to use an application + * @param from - address of sender + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param appIndex - the ID of the app to join + * @param appArgs - Array of Uint8Array, any additional arguments to the application + * @param accounts - Array of Address strings, any additional accounts to supply to the application + * @param foreignApps - Array of int, any other apps used by the application, identified by index + * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param note - Arbitrary data for sender to store + * @param lease - Lease a transaction + * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions + * @param boxes - Array of BoxReference, app ID and name of box to be accessed + */ +export function makeApplicationOptInTxn( + from: AppOptInTxn['from'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + appIndex: AppOptInTxn['appIndex'], + appArgs?: AppOptInTxn['appArgs'], + accounts?: AppOptInTxn['appAccounts'], + foreignApps?: AppOptInTxn['appForeignApps'], + foreignAssets?: AppOptInTxn['appForeignAssets'], + note?: AppOptInTxn['note'], + lease?: AppOptInTxn['lease'], + rekeyTo?: AppOptInTxn['reKeyTo'], + boxes?: AppOptInTxn['boxes'] +) { + const o: AppOptInTxn = { + type: TransactionType.appl, + from, + suggestedParams, + appIndex, + appOnComplete: OnApplicationComplete.OptInOC, + appArgs, + appAccounts: accounts, + appForeignApps: foreignApps, + appForeignAssets: foreignAssets, + boxes, + note, + lease, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeApplicationOptInTxn, instead accepting an argument object +export function makeApplicationOptInTxnFromObject( + o: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + appAccounts: 'accounts'; + appForeignApps: 'foreignApps'; + appForeignAssets: 'foreignAssets'; + reKeyTo: 'rekeyTo'; + } + >, + | 'from' + | 'suggestedParams' + | 'appIndex' + | 'appArgs' + | 'accounts' + | 'foreignApps' + | 'foreignAssets' + | 'boxes' + | 'note' + | 'lease' + | 'rekeyTo' + > + > +) { + return makeApplicationOptInTxn( + o.from, + o.suggestedParams, + o.appIndex, + o.appArgs, + o.accounts, + o.foreignApps, + o.foreignAssets, + o.note, + o.lease, + o.rekeyTo, + o.boxes + ); +} + +/** + * Make a transaction that closes out a user's state in an application + * @param from - address of sender + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param appIndex - the ID of the app to use + * @param appArgs - Array of Uint8Array, any additional arguments to the application + * @param accounts - Array of Address strings, any additional accounts to supply to the application + * @param foreignApps - Array of int, any other apps used by the application, identified by index + * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param note - Arbitrary data for sender to store + * @param lease - Lease a transaction + * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions + * @param boxes - Array of BoxReference, app ID and name of box to be accessed + */ +export function makeApplicationCloseOutTxn( + from: AppCloseOutTxn['from'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + appIndex: AppCloseOutTxn['appIndex'], + appArgs?: AppCloseOutTxn['appArgs'], + accounts?: AppCloseOutTxn['appAccounts'], + foreignApps?: AppCloseOutTxn['appForeignApps'], + foreignAssets?: AppCloseOutTxn['appForeignAssets'], + note?: AppCloseOutTxn['note'], + lease?: AppCloseOutTxn['lease'], + rekeyTo?: AppCloseOutTxn['reKeyTo'], + boxes?: AppCloseOutTxn['boxes'] +) { + const o: AppCloseOutTxn = { + type: TransactionType.appl, + from, + suggestedParams, + appIndex, + appOnComplete: OnApplicationComplete.CloseOutOC, + appArgs, + appAccounts: accounts, + appForeignApps: foreignApps, + appForeignAssets: foreignAssets, + boxes, + note, + lease, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeApplicationCloseOutTxn, instead accepting an argument object +export function makeApplicationCloseOutTxnFromObject( + o: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + appAccounts: 'accounts'; + appForeignApps: 'foreignApps'; + appForeignAssets: 'foreignAssets'; + reKeyTo: 'rekeyTo'; + } + >, + | 'from' + | 'suggestedParams' + | 'appIndex' + | 'appArgs' + | 'accounts' + | 'foreignApps' + | 'foreignAssets' + | 'boxes' + | 'note' + | 'lease' + | 'rekeyTo' + > + > +) { + return makeApplicationCloseOutTxn( + o.from, + o.suggestedParams, + o.appIndex, + o.appArgs, + o.accounts, + o.foreignApps, + o.foreignAssets, + o.note, + o.lease, + o.rekeyTo, + o.boxes + ); +} + +/** + * Make a transaction that clears a user's state in an application + * @param from - address of sender + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param appIndex - the ID of the app to use + * @param appArgs - Array of Uint8Array, any additional arguments to the application + * @param accounts - Array of Address strings, any additional accounts to supply to the application + * @param foreignApps - Array of int, any other apps used by the application, identified by index + * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param note - Arbitrary data for sender to store + * @param lease - Lease a transaction + * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions + * @param boxes - Array of BoxReference, app ID and name of box to be accessed + */ +export function makeApplicationClearStateTxn( + from: AppClearStateTxn['from'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + appIndex: AppClearStateTxn['appIndex'], + appArgs?: AppClearStateTxn['appArgs'], + accounts?: AppClearStateTxn['appAccounts'], + foreignApps?: AppClearStateTxn['appForeignApps'], + foreignAssets?: AppClearStateTxn['appForeignAssets'], + note?: AppClearStateTxn['note'], + lease?: AppClearStateTxn['lease'], + rekeyTo?: AppClearStateTxn['reKeyTo'], + boxes?: AppClearStateTxn['boxes'] +) { + const o: AppClearStateTxn = { + type: TransactionType.appl, + from, + suggestedParams, + appIndex, + appOnComplete: OnApplicationComplete.ClearStateOC, + appArgs, + appAccounts: accounts, + appForeignApps: foreignApps, + appForeignAssets: foreignAssets, + boxes, + note, + lease, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeApplicationClearStateTxn, instead accepting an argument object +export function makeApplicationClearStateTxnFromObject( + o: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + appAccounts: 'accounts'; + appForeignApps: 'foreignApps'; + appForeignAssets: 'foreignAssets'; + reKeyTo: 'rekeyTo'; + } + >, + | 'from' + | 'suggestedParams' + | 'appIndex' + | 'appArgs' + | 'accounts' + | 'foreignApps' + | 'foreignAssets' + | 'boxes' + | 'note' + | 'lease' + | 'rekeyTo' + > + > +) { + return makeApplicationClearStateTxn( + o.from, + o.suggestedParams, + o.appIndex, + o.appArgs, + o.accounts, + o.foreignApps, + o.foreignAssets, + o.note, + o.lease, + o.rekeyTo, + o.boxes + ); +} + +/** + * Make a transaction that just calls an application, doing nothing on completion + * @param from - address of sender + * @param suggestedParams - a dict holding common-to-all-txns args: + * fee - integer fee per byte, in microAlgos. for a flat fee, set flatFee to true + * flatFee - bool optionally set this to true to specify fee as microalgos-per-txn + * If true, txn fee may fall below the ALGORAND_MIN_TX_FEE + * firstRound - integer first protocol round on which this txn is valid + * lastRound - integer last protocol round on which this txn is valid + * genesisHash - string specifies hash genesis block of network in use + * genesisID - string specifies genesis ID of network in use + * @param appIndex - the ID of the app to use + * @param appArgs - Array of Uint8Array, any additional arguments to the application + * @param accounts - Array of Address strings, any additional accounts to supply to the application + * @param foreignApps - Array of int, any other apps used by the application, identified by index + * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param note - Arbitrary data for sender to store + * @param lease - Lease a transaction + * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions + * @param boxes - Array of BoxReference, app ID and name of box to be accessed + */ +export function makeApplicationNoOpTxn( + from: AppNoOpTxn['from'], + suggestedParams: MustHaveSuggestedParams['suggestedParams'], + appIndex: AppNoOpTxn['appIndex'], + appArgs?: AppNoOpTxn['appArgs'], + accounts?: AppNoOpTxn['appAccounts'], + foreignApps?: AppNoOpTxn['appForeignApps'], + foreignAssets?: AppNoOpTxn['appForeignAssets'], + note?: AppNoOpTxn['note'], + lease?: AppNoOpTxn['lease'], + rekeyTo?: AppNoOpTxn['reKeyTo'], + boxes?: AppNoOpTxn['boxes'] +) { + const o: AppNoOpTxn = { + type: TransactionType.appl, + from, + suggestedParams, + appIndex, + appOnComplete: OnApplicationComplete.NoOpOC, + appArgs, + appAccounts: accounts, + appForeignApps: foreignApps, + appForeignAssets: foreignAssets, + boxes, + note, + lease, + reKeyTo: rekeyTo, + }; + return new txnBuilder.Transaction(o); +} + +// helper for above makeApplicationNoOpTxn, instead accepting an argument object +export function makeApplicationNoOpTxnFromObject( + o: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + appAccounts: 'accounts'; + appForeignApps: 'foreignApps'; + appForeignAssets: 'foreignAssets'; + reKeyTo: 'rekeyTo'; + } + >, + | 'from' + | 'suggestedParams' + | 'appIndex' + | 'appArgs' + | 'accounts' + | 'foreignApps' + | 'foreignAssets' + | 'boxes' + | 'note' + | 'lease' + | 'rekeyTo' + > + > +) { + return makeApplicationNoOpTxn( + o.from, + o.suggestedParams, + o.appIndex, + o.appArgs, + o.accounts, + o.foreignApps, + o.foreignAssets, + o.note, + o.lease, + o.rekeyTo, + o.boxes + ); +} + +export { OnApplicationComplete } from './types/transactions/base'; + +/** + * Generic function for creating any application call transaction. + */ +export function makeApplicationCallTxnFromObject( + options: Expand< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + appOnComplete: 'onComplete'; + appAccounts: 'accounts'; + appForeignApps: 'foreignApps'; + appForeignAssets: 'foreignAssets'; + reKeyTo: 'rekeyTo'; + } + >, + | 'from' + | 'suggestedParams' + | 'appIndex' + | 'onComplete' + | 'appArgs' + | 'accounts' + | 'foreignApps' + | 'foreignAssets' + | 'boxes' + | 'note' + | 'lease' + | 'rekeyTo' + | 'extraPages' + > & + Partial< + Pick< + RenameProperties< + MustHaveSuggestedParams, + { + appApprovalProgram: 'approvalProgram'; + appClearProgram: 'clearProgram'; + appLocalInts: 'numLocalInts'; + appLocalByteSlices: 'numLocalByteSlices'; + appGlobalInts: 'numGlobalInts'; + appGlobalByteSlices: 'numGlobalByteSlices'; + } + >, + | 'approvalProgram' + | 'clearProgram' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + > + > + > +) { + const o: AppCreateTxn = { + type: TransactionType.appl, + from: options.from, + suggestedParams: options.suggestedParams, + appIndex: options.appIndex, + appOnComplete: options.onComplete, + appLocalInts: options.numLocalInts, + appLocalByteSlices: options.numLocalByteSlices, + appGlobalInts: options.numGlobalInts, + appGlobalByteSlices: options.numGlobalByteSlices, + appApprovalProgram: options.approvalProgram, + appClearProgram: options.clearProgram, + appArgs: options.appArgs, + appAccounts: options.accounts, + appForeignApps: options.foreignApps, + appForeignAssets: options.foreignAssets, + boxes: options.boxes, + note: options.note, + lease: options.lease, + reKeyTo: options.rekeyTo, + extraPages: options.extraPages, + }; + return new txnBuilder.Transaction(o); +} diff --git a/src/mnemonic/mnemonic.ts b/src/mnemonic/mnemonic.ts new file mode 100644 index 0000000..92bfa55 --- /dev/null +++ b/src/mnemonic/mnemonic.ts @@ -0,0 +1,181 @@ +/* eslint-disable no-bitwise */ +import english from './wordlists/english'; +import * as nacl from '../nacl/naclWrappers'; +import * as address from '../encoding/address'; +import Account from '../types/account'; + +export const FAIL_TO_DECODE_MNEMONIC_ERROR_MSG = 'failed to decode mnemonic'; +export const NOT_IN_WORDS_LIST_ERROR_MSG = + 'the mnemonic contains a word that is not in the wordlist'; + +// https://stackoverflow.com/a/51452614 +function toUint11Array(buffer8: Uint8Array | number[]) { + const buffer11 = []; + let acc = 0; + let accBits = 0; + function add(octet) { + acc |= octet << accBits; + accBits += 8; + if (accBits >= 11) { + buffer11.push(acc & 0x7ff); + acc >>= 11; + accBits -= 11; + } + } + function flush() { + if (accBits) { + buffer11.push(acc); + } + } + + buffer8.forEach(add); + flush(); + return buffer11; +} + +function applyWords(nums: number[]) { + return nums.map((n) => english[n]); +} + +function computeChecksum(seed: Uint8Array) { + const hashBuffer = nacl.genericHash(seed); + const uint11Hash = toUint11Array(hashBuffer); + const words = applyWords(uint11Hash); + + return words[0]; +} + +/** + * mnemonicFromSeed converts a 32-byte key into a 25 word mnemonic. The generated mnemonic includes a checksum. + * Each word in the mnemonic represents 11 bits of data, and the last 11 bits are reserved for the checksum. + * @param seed - 32 bytes long seed + * @returns 25 words mnemonic + */ +export function mnemonicFromSeed(seed: Uint8Array) { + // Sanity length check + if (seed.length !== nacl.SEED_BTYES_LENGTH) { + throw new RangeError(`Seed length must be ${nacl.SEED_BTYES_LENGTH}`); + } + + const uint11Array = toUint11Array(seed); + const words = applyWords(uint11Array); + const checksumWord = computeChecksum(seed); + + return `${words.join(' ')} ${checksumWord}`; +} + +// from Uint11Array +// https://stackoverflow.com/a/51452614 +function toUint8Array(buffer11: number[]) { + const buffer8 = []; + let acc = 0; + let accBits = 0; + function add(ui11) { + acc |= ui11 << accBits; + accBits += 11; + while (accBits >= 8) { + buffer8.push(acc & 0xff); + acc >>= 8; + accBits -= 8; + } + } + function flush() { + if (accBits) { + buffer8.push(acc); + } + } + + buffer11.forEach(add); + flush(); + return new Uint8Array(buffer8); +} + +/** + * seedFromMnemonic converts a mnemonic generated using this library into the source key used to create it. + * It returns an error if the passed mnemonic has an incorrect checksum, if the number of words is unexpected, or if one + * of the passed words is not found in the words list. + * @param mnemonic - 25 words mnemonic + * @returns 32 bytes long seed + */ +export function seedFromMnemonic(mnemonic: string) { + const words = mnemonic.split(' '); + const key = words.slice(0, 24); + + // Check that all words are in list + for (const w of key) { + if (english.indexOf(w) === -1) throw new Error(NOT_IN_WORDS_LIST_ERROR_MSG); + } + + const checksum = words[words.length - 1]; + const uint11Array = key.map((word) => english.indexOf(word)); + + // Convert the key to uint8Array + let uint8Array = toUint8Array(uint11Array); + + // We need to chop the last byte - + // the short explanation - Since 256 is not divisible by 11, we have an extra 0x0 byte. + // The longer explanation - When splitting the 256 bits to chunks of 11, we get 23 words and a left over of 3 bits. + // This left gets padded with another 8 bits to the create the 24th word. + // While converting back to byte array, our new 264 bits array is divisible by 8 but the last byte is just the padding. + + // check that we have 33 bytes long array as expected + if (uint8Array.length !== 33) + throw new Error(FAIL_TO_DECODE_MNEMONIC_ERROR_MSG); + + // check that the last byte is actually 0x0 + if (uint8Array[uint8Array.length - 1] !== 0x0) + throw new Error(FAIL_TO_DECODE_MNEMONIC_ERROR_MSG); + + // chop it ! + uint8Array = uint8Array.slice(0, uint8Array.length - 1); + + // compute checksum + const cs = computeChecksum(uint8Array); + + // success! + if (cs === checksum) return uint8Array; + + throw new Error(FAIL_TO_DECODE_MNEMONIC_ERROR_MSG); +} + +/** + * mnemonicToSecretKey takes a mnemonic string and returns the corresponding Algorand address and its secret key. + * @param mn - 25 words Algorand mnemonic + * @throws error if fails to decode the mnemonic + */ +export function mnemonicToSecretKey(mn: string): Account { + const seed = seedFromMnemonic(mn); + const keys = nacl.keyPairFromSeed(seed); + const encodedPk = address.encodeAddress(keys.publicKey); + return { addr: encodedPk, sk: keys.secretKey }; +} + +/** + * secretKeyToMnemonic takes an Algorand secret key and returns the corresponding mnemonic. + * @param sk - Algorand secret key + * @returns Secret key's associated mnemonic + */ +export function secretKeyToMnemonic(sk: Uint8Array) { + // get the seed from the sk + const seed = sk.slice(0, nacl.SEED_BTYES_LENGTH); + return mnemonicFromSeed(seed); +} + +/** + * mnemonicToMasterDerivationKey takes a mnemonic string and returns the corresponding master derivation key. + * @param mn - 25 words Algorand mnemonic + * @returns Uint8Array + * @throws error if fails to decode the mnemonic + */ +export function mnemonicToMasterDerivationKey(mn: string) { + return seedFromMnemonic(mn); +} + +/** + * masterDerivationKeyToMnemonic takes a master derivation key and returns the corresponding mnemonic. + * @param mdk - Uint8Array + * @returns string mnemonic + */ +export function masterDerivationKeyToMnemonic(mdk: Uint8Array) { + return mnemonicFromSeed(mdk); +} diff --git a/src/mnemonic/wordlists/english.ts b/src/mnemonic/wordlists/english.ts new file mode 100644 index 0000000..99954e8 --- /dev/null +++ b/src/mnemonic/wordlists/english.ts @@ -0,0 +1,2052 @@ +const english = [ + 'abandon', + 'ability', + 'able', + 'about', + 'above', + 'absent', + 'absorb', + 'abstract', + 'absurd', + 'abuse', + 'access', + 'accident', + 'account', + 'accuse', + 'achieve', + 'acid', + 'acoustic', + 'acquire', + 'across', + 'act', + 'action', + 'actor', + 'actress', + 'actual', + 'adapt', + 'add', + 'addict', + 'address', + 'adjust', + 'admit', + 'adult', + 'advance', + 'advice', + 'aerobic', + 'affair', + 'afford', + 'afraid', + 'again', + 'age', + 'agent', + 'agree', + 'ahead', + 'aim', + 'air', + 'airport', + 'aisle', + 'alarm', + 'album', + 'alcohol', + 'alert', + 'alien', + 'all', + 'alley', + 'allow', + 'almost', + 'alone', + 'alpha', + 'already', + 'also', + 'alter', + 'always', + 'amateur', + 'amazing', + 'among', + 'amount', + 'amused', + 'analyst', + 'anchor', + 'ancient', + 'anger', + 'angle', + 'angry', + 'animal', + 'ankle', + 'announce', + 'annual', + 'another', + 'answer', + 'antenna', + 'antique', + 'anxiety', + 'any', + 'apart', + 'apology', + 'appear', + 'apple', + 'approve', + 'april', + 'arch', + 'arctic', + 'area', + 'arena', + 'argue', + 'arm', + 'armed', + 'armor', + 'army', + 'around', + 'arrange', + 'arrest', + 'arrive', + 'arrow', + 'art', + 'artefact', + 'artist', + 'artwork', + 'ask', + 'aspect', + 'assault', + 'asset', + 'assist', + 'assume', + 'asthma', + 'athlete', + 'atom', + 'attack', + 'attend', + 'attitude', + 'attract', + 'auction', + 'audit', + 'august', + 'aunt', + 'author', + 'auto', + 'autumn', + 'average', + 'avocado', + 'avoid', + 'awake', + 'aware', + 'away', + 'awesome', + 'awful', + 'awkward', + 'axis', + 'baby', + 'bachelor', + 'bacon', + 'badge', + 'bag', + 'balance', + 'balcony', + 'ball', + 'bamboo', + 'banana', + 'banner', + 'bar', + 'barely', + 'bargain', + 'barrel', + 'base', + 'basic', + 'basket', + 'battle', + 'beach', + 'bean', + 'beauty', + 'because', + 'become', + 'beef', + 'before', + 'begin', + 'behave', + 'behind', + 'believe', + 'below', + 'belt', + 'bench', + 'benefit', + 'best', + 'betray', + 'better', + 'between', + 'beyond', + 'bicycle', + 'bid', + 'bike', + 'bind', + 'biology', + 'bird', + 'birth', + 'bitter', + 'black', + 'blade', + 'blame', + 'blanket', + 'blast', + 'bleak', + 'bless', + 'blind', + 'blood', + 'blossom', + 'blouse', + 'blue', + 'blur', + 'blush', + 'board', + 'boat', + 'body', + 'boil', + 'bomb', + 'bone', + 'bonus', + 'book', + 'boost', + 'border', + 'boring', + 'borrow', + 'boss', + 'bottom', + 'bounce', + 'box', + 'boy', + 'bracket', + 'brain', + 'brand', + 'brass', + 'brave', + 'bread', + 'breeze', + 'brick', + 'bridge', + 'brief', + 'bright', + 'bring', + 'brisk', + 'broccoli', + 'broken', + 'bronze', + 'broom', + 'brother', + 'brown', + 'brush', + 'bubble', + 'buddy', + 'budget', + 'buffalo', + 'build', + 'bulb', + 'bulk', + 'bullet', + 'bundle', + 'bunker', + 'burden', + 'burger', + 'burst', + 'bus', + 'business', + 'busy', + 'butter', + 'buyer', + 'buzz', + 'cabbage', + 'cabin', + 'cable', + 'cactus', + 'cage', + 'cake', + 'call', + 'calm', + 'camera', + 'camp', + 'can', + 'canal', + 'cancel', + 'candy', + 'cannon', + 'canoe', + 'canvas', + 'canyon', + 'capable', + 'capital', + 'captain', + 'car', + 'carbon', + 'card', + 'cargo', + 'carpet', + 'carry', + 'cart', + 'case', + 'cash', + 'casino', + 'castle', + 'casual', + 'cat', + 'catalog', + 'catch', + 'category', + 'cattle', + 'caught', + 'cause', + 'caution', + 'cave', + 'ceiling', + 'celery', + 'cement', + 'census', + 'century', + 'cereal', + 'certain', + 'chair', + 'chalk', + 'champion', + 'change', + 'chaos', + 'chapter', + 'charge', + 'chase', + 'chat', + 'cheap', + 'check', + 'cheese', + 'chef', + 'cherry', + 'chest', + 'chicken', + 'chief', + 'child', + 'chimney', + 'choice', + 'choose', + 'chronic', + 'chuckle', + 'chunk', + 'churn', + 'cigar', + 'cinnamon', + 'circle', + 'citizen', + 'city', + 'civil', + 'claim', + 'clap', + 'clarify', + 'claw', + 'clay', + 'clean', + 'clerk', + 'clever', + 'click', + 'client', + 'cliff', + 'climb', + 'clinic', + 'clip', + 'clock', + 'clog', + 'close', + 'cloth', + 'cloud', + 'clown', + 'club', + 'clump', + 'cluster', + 'clutch', + 'coach', + 'coast', + 'coconut', + 'code', + 'coffee', + 'coil', + 'coin', + 'collect', + 'color', + 'column', + 'combine', + 'come', + 'comfort', + 'comic', + 'common', + 'company', + 'concert', + 'conduct', + 'confirm', + 'congress', + 'connect', + 'consider', + 'control', + 'convince', + 'cook', + 'cool', + 'copper', + 'copy', + 'coral', + 'core', + 'corn', + 'correct', + 'cost', + 'cotton', + 'couch', + 'country', + 'couple', + 'course', + 'cousin', + 'cover', + 'coyote', + 'crack', + 'cradle', + 'craft', + 'cram', + 'crane', + 'crash', + 'crater', + 'crawl', + 'crazy', + 'cream', + 'credit', + 'creek', + 'crew', + 'cricket', + 'crime', + 'crisp', + 'critic', + 'crop', + 'cross', + 'crouch', + 'crowd', + 'crucial', + 'cruel', + 'cruise', + 'crumble', + 'crunch', + 'crush', + 'cry', + 'crystal', + 'cube', + 'culture', + 'cup', + 'cupboard', + 'curious', + 'current', + 'curtain', + 'curve', + 'cushion', + 'custom', + 'cute', + 'cycle', + 'dad', + 'damage', + 'damp', + 'dance', + 'danger', + 'daring', + 'dash', + 'daughter', + 'dawn', + 'day', + 'deal', + 'debate', + 'debris', + 'decade', + 'december', + 'decide', + 'decline', + 'decorate', + 'decrease', + 'deer', + 'defense', + 'define', + 'defy', + 'degree', + 'delay', + 'deliver', + 'demand', + 'demise', + 'denial', + 'dentist', + 'deny', + 'depart', + 'depend', + 'deposit', + 'depth', + 'deputy', + 'derive', + 'describe', + 'desert', + 'design', + 'desk', + 'despair', + 'destroy', + 'detail', + 'detect', + 'develop', + 'device', + 'devote', + 'diagram', + 'dial', + 'diamond', + 'diary', + 'dice', + 'diesel', + 'diet', + 'differ', + 'digital', + 'dignity', + 'dilemma', + 'dinner', + 'dinosaur', + 'direct', + 'dirt', + 'disagree', + 'discover', + 'disease', + 'dish', + 'dismiss', + 'disorder', + 'display', + 'distance', + 'divert', + 'divide', + 'divorce', + 'dizzy', + 'doctor', + 'document', + 'dog', + 'doll', + 'dolphin', + 'domain', + 'donate', + 'donkey', + 'donor', + 'door', + 'dose', + 'double', + 'dove', + 'draft', + 'dragon', + 'drama', + 'drastic', + 'draw', + 'dream', + 'dress', + 'drift', + 'drill', + 'drink', + 'drip', + 'drive', + 'drop', + 'drum', + 'dry', + 'duck', + 'dumb', + 'dune', + 'during', + 'dust', + 'dutch', + 'duty', + 'dwarf', + 'dynamic', + 'eager', + 'eagle', + 'early', + 'earn', + 'earth', + 'easily', + 'east', + 'easy', + 'echo', + 'ecology', + 'economy', + 'edge', + 'edit', + 'educate', + 'effort', + 'egg', + 'eight', + 'either', + 'elbow', + 'elder', + 'electric', + 'elegant', + 'element', + 'elephant', + 'elevator', + 'elite', + 'else', + 'embark', + 'embody', + 'embrace', + 'emerge', + 'emotion', + 'employ', + 'empower', + 'empty', + 'enable', + 'enact', + 'end', + 'endless', + 'endorse', + 'enemy', + 'energy', + 'enforce', + 'engage', + 'engine', + 'enhance', + 'enjoy', + 'enlist', + 'enough', + 'enrich', + 'enroll', + 'ensure', + 'enter', + 'entire', + 'entry', + 'envelope', + 'episode', + 'equal', + 'equip', + 'era', + 'erase', + 'erode', + 'erosion', + 'error', + 'erupt', + 'escape', + 'essay', + 'essence', + 'estate', + 'eternal', + 'ethics', + 'evidence', + 'evil', + 'evoke', + 'evolve', + 'exact', + 'example', + 'excess', + 'exchange', + 'excite', + 'exclude', + 'excuse', + 'execute', + 'exercise', + 'exhaust', + 'exhibit', + 'exile', + 'exist', + 'exit', + 'exotic', + 'expand', + 'expect', + 'expire', + 'explain', + 'expose', + 'express', + 'extend', + 'extra', + 'eye', + 'eyebrow', + 'fabric', + 'face', + 'faculty', + 'fade', + 'faint', + 'faith', + 'fall', + 'false', + 'fame', + 'family', + 'famous', + 'fan', + 'fancy', + 'fantasy', + 'farm', + 'fashion', + 'fat', + 'fatal', + 'father', + 'fatigue', + 'fault', + 'favorite', + 'feature', + 'february', + 'federal', + 'fee', + 'feed', + 'feel', + 'female', + 'fence', + 'festival', + 'fetch', + 'fever', + 'few', + 'fiber', + 'fiction', + 'field', + 'figure', + 'file', + 'film', + 'filter', + 'final', + 'find', + 'fine', + 'finger', + 'finish', + 'fire', + 'firm', + 'first', + 'fiscal', + 'fish', + 'fit', + 'fitness', + 'fix', + 'flag', + 'flame', + 'flash', + 'flat', + 'flavor', + 'flee', + 'flight', + 'flip', + 'float', + 'flock', + 'floor', + 'flower', + 'fluid', + 'flush', + 'fly', + 'foam', + 'focus', + 'fog', + 'foil', + 'fold', + 'follow', + 'food', + 'foot', + 'force', + 'forest', + 'forget', + 'fork', + 'fortune', + 'forum', + 'forward', + 'fossil', + 'foster', + 'found', + 'fox', + 'fragile', + 'frame', + 'frequent', + 'fresh', + 'friend', + 'fringe', + 'frog', + 'front', + 'frost', + 'frown', + 'frozen', + 'fruit', + 'fuel', + 'fun', + 'funny', + 'furnace', + 'fury', + 'future', + 'gadget', + 'gain', + 'galaxy', + 'gallery', + 'game', + 'gap', + 'garage', + 'garbage', + 'garden', + 'garlic', + 'garment', + 'gas', + 'gasp', + 'gate', + 'gather', + 'gauge', + 'gaze', + 'general', + 'genius', + 'genre', + 'gentle', + 'genuine', + 'gesture', + 'ghost', + 'giant', + 'gift', + 'giggle', + 'ginger', + 'giraffe', + 'girl', + 'give', + 'glad', + 'glance', + 'glare', + 'glass', + 'glide', + 'glimpse', + 'globe', + 'gloom', + 'glory', + 'glove', + 'glow', + 'glue', + 'goat', + 'goddess', + 'gold', + 'good', + 'goose', + 'gorilla', + 'gospel', + 'gossip', + 'govern', + 'gown', + 'grab', + 'grace', + 'grain', + 'grant', + 'grape', + 'grass', + 'gravity', + 'great', + 'green', + 'grid', + 'grief', + 'grit', + 'grocery', + 'group', + 'grow', + 'grunt', + 'guard', + 'guess', + 'guide', + 'guilt', + 'guitar', + 'gun', + 'gym', + 'habit', + 'hair', + 'half', + 'hammer', + 'hamster', + 'hand', + 'happy', + 'harbor', + 'hard', + 'harsh', + 'harvest', + 'hat', + 'have', + 'hawk', + 'hazard', + 'head', + 'health', + 'heart', + 'heavy', + 'hedgehog', + 'height', + 'hello', + 'helmet', + 'help', + 'hen', + 'hero', + 'hidden', + 'high', + 'hill', + 'hint', + 'hip', + 'hire', + 'history', + 'hobby', + 'hockey', + 'hold', + 'hole', + 'holiday', + 'hollow', + 'home', + 'honey', + 'hood', + 'hope', + 'horn', + 'horror', + 'horse', + 'hospital', + 'host', + 'hotel', + 'hour', + 'hover', + 'hub', + 'huge', + 'human', + 'humble', + 'humor', + 'hundred', + 'hungry', + 'hunt', + 'hurdle', + 'hurry', + 'hurt', + 'husband', + 'hybrid', + 'ice', + 'icon', + 'idea', + 'identify', + 'idle', + 'ignore', + 'ill', + 'illegal', + 'illness', + 'image', + 'imitate', + 'immense', + 'immune', + 'impact', + 'impose', + 'improve', + 'impulse', + 'inch', + 'include', + 'income', + 'increase', + 'index', + 'indicate', + 'indoor', + 'industry', + 'infant', + 'inflict', + 'inform', + 'inhale', + 'inherit', + 'initial', + 'inject', + 'injury', + 'inmate', + 'inner', + 'innocent', + 'input', + 'inquiry', + 'insane', + 'insect', + 'inside', + 'inspire', + 'install', + 'intact', + 'interest', + 'into', + 'invest', + 'invite', + 'involve', + 'iron', + 'island', + 'isolate', + 'issue', + 'item', + 'ivory', + 'jacket', + 'jaguar', + 'jar', + 'jazz', + 'jealous', + 'jeans', + 'jelly', + 'jewel', + 'job', + 'join', + 'joke', + 'journey', + 'joy', + 'judge', + 'juice', + 'jump', + 'jungle', + 'junior', + 'junk', + 'just', + 'kangaroo', + 'keen', + 'keep', + 'ketchup', + 'key', + 'kick', + 'kid', + 'kidney', + 'kind', + 'kingdom', + 'kiss', + 'kit', + 'kitchen', + 'kite', + 'kitten', + 'kiwi', + 'knee', + 'knife', + 'knock', + 'know', + 'lab', + 'label', + 'labor', + 'ladder', + 'lady', + 'lake', + 'lamp', + 'language', + 'laptop', + 'large', + 'later', + 'latin', + 'laugh', + 'laundry', + 'lava', + 'law', + 'lawn', + 'lawsuit', + 'layer', + 'lazy', + 'leader', + 'leaf', + 'learn', + 'leave', + 'lecture', + 'left', + 'leg', + 'legal', + 'legend', + 'leisure', + 'lemon', + 'lend', + 'length', + 'lens', + 'leopard', + 'lesson', + 'letter', + 'level', + 'liar', + 'liberty', + 'library', + 'license', + 'life', + 'lift', + 'light', + 'like', + 'limb', + 'limit', + 'link', + 'lion', + 'liquid', + 'list', + 'little', + 'live', + 'lizard', + 'load', + 'loan', + 'lobster', + 'local', + 'lock', + 'logic', + 'lonely', + 'long', + 'loop', + 'lottery', + 'loud', + 'lounge', + 'love', + 'loyal', + 'lucky', + 'luggage', + 'lumber', + 'lunar', + 'lunch', + 'luxury', + 'lyrics', + 'machine', + 'mad', + 'magic', + 'magnet', + 'maid', + 'mail', + 'main', + 'major', + 'make', + 'mammal', + 'man', + 'manage', + 'mandate', + 'mango', + 'mansion', + 'manual', + 'maple', + 'marble', + 'march', + 'margin', + 'marine', + 'market', + 'marriage', + 'mask', + 'mass', + 'master', + 'match', + 'material', + 'math', + 'matrix', + 'matter', + 'maximum', + 'maze', + 'meadow', + 'mean', + 'measure', + 'meat', + 'mechanic', + 'medal', + 'media', + 'melody', + 'melt', + 'member', + 'memory', + 'mention', + 'menu', + 'mercy', + 'merge', + 'merit', + 'merry', + 'mesh', + 'message', + 'metal', + 'method', + 'middle', + 'midnight', + 'milk', + 'million', + 'mimic', + 'mind', + 'minimum', + 'minor', + 'minute', + 'miracle', + 'mirror', + 'misery', + 'miss', + 'mistake', + 'mix', + 'mixed', + 'mixture', + 'mobile', + 'model', + 'modify', + 'mom', + 'moment', + 'monitor', + 'monkey', + 'monster', + 'month', + 'moon', + 'moral', + 'more', + 'morning', + 'mosquito', + 'mother', + 'motion', + 'motor', + 'mountain', + 'mouse', + 'move', + 'movie', + 'much', + 'muffin', + 'mule', + 'multiply', + 'muscle', + 'museum', + 'mushroom', + 'music', + 'must', + 'mutual', + 'myself', + 'mystery', + 'myth', + 'naive', + 'name', + 'napkin', + 'narrow', + 'nasty', + 'nation', + 'nature', + 'near', + 'neck', + 'need', + 'negative', + 'neglect', + 'neither', + 'nephew', + 'nerve', + 'nest', + 'net', + 'network', + 'neutral', + 'never', + 'news', + 'next', + 'nice', + 'night', + 'noble', + 'noise', + 'nominee', + 'noodle', + 'normal', + 'north', + 'nose', + 'notable', + 'note', + 'nothing', + 'notice', + 'novel', + 'now', + 'nuclear', + 'number', + 'nurse', + 'nut', + 'oak', + 'obey', + 'object', + 'oblige', + 'obscure', + 'observe', + 'obtain', + 'obvious', + 'occur', + 'ocean', + 'october', + 'odor', + 'off', + 'offer', + 'office', + 'often', + 'oil', + 'okay', + 'old', + 'olive', + 'olympic', + 'omit', + 'once', + 'one', + 'onion', + 'online', + 'only', + 'open', + 'opera', + 'opinion', + 'oppose', + 'option', + 'orange', + 'orbit', + 'orchard', + 'order', + 'ordinary', + 'organ', + 'orient', + 'original', + 'orphan', + 'ostrich', + 'other', + 'outdoor', + 'outer', + 'output', + 'outside', + 'oval', + 'oven', + 'over', + 'own', + 'owner', + 'oxygen', + 'oyster', + 'ozone', + 'pact', + 'paddle', + 'page', + 'pair', + 'palace', + 'palm', + 'panda', + 'panel', + 'panic', + 'panther', + 'paper', + 'parade', + 'parent', + 'park', + 'parrot', + 'party', + 'pass', + 'patch', + 'path', + 'patient', + 'patrol', + 'pattern', + 'pause', + 'pave', + 'payment', + 'peace', + 'peanut', + 'pear', + 'peasant', + 'pelican', + 'pen', + 'penalty', + 'pencil', + 'people', + 'pepper', + 'perfect', + 'permit', + 'person', + 'pet', + 'phone', + 'photo', + 'phrase', + 'physical', + 'piano', + 'picnic', + 'picture', + 'piece', + 'pig', + 'pigeon', + 'pill', + 'pilot', + 'pink', + 'pioneer', + 'pipe', + 'pistol', + 'pitch', + 'pizza', + 'place', + 'planet', + 'plastic', + 'plate', + 'play', + 'please', + 'pledge', + 'pluck', + 'plug', + 'plunge', + 'poem', + 'poet', + 'point', + 'polar', + 'pole', + 'police', + 'pond', + 'pony', + 'pool', + 'popular', + 'portion', + 'position', + 'possible', + 'post', + 'potato', + 'pottery', + 'poverty', + 'powder', + 'power', + 'practice', + 'praise', + 'predict', + 'prefer', + 'prepare', + 'present', + 'pretty', + 'prevent', + 'price', + 'pride', + 'primary', + 'print', + 'priority', + 'prison', + 'private', + 'prize', + 'problem', + 'process', + 'produce', + 'profit', + 'program', + 'project', + 'promote', + 'proof', + 'property', + 'prosper', + 'protect', + 'proud', + 'provide', + 'public', + 'pudding', + 'pull', + 'pulp', + 'pulse', + 'pumpkin', + 'punch', + 'pupil', + 'puppy', + 'purchase', + 'purity', + 'purpose', + 'purse', + 'push', + 'put', + 'puzzle', + 'pyramid', + 'quality', + 'quantum', + 'quarter', + 'question', + 'quick', + 'quit', + 'quiz', + 'quote', + 'rabbit', + 'raccoon', + 'race', + 'rack', + 'radar', + 'radio', + 'rail', + 'rain', + 'raise', + 'rally', + 'ramp', + 'ranch', + 'random', + 'range', + 'rapid', + 'rare', + 'rate', + 'rather', + 'raven', + 'raw', + 'razor', + 'ready', + 'real', + 'reason', + 'rebel', + 'rebuild', + 'recall', + 'receive', + 'recipe', + 'record', + 'recycle', + 'reduce', + 'reflect', + 'reform', + 'refuse', + 'region', + 'regret', + 'regular', + 'reject', + 'relax', + 'release', + 'relief', + 'rely', + 'remain', + 'remember', + 'remind', + 'remove', + 'render', + 'renew', + 'rent', + 'reopen', + 'repair', + 'repeat', + 'replace', + 'report', + 'require', + 'rescue', + 'resemble', + 'resist', + 'resource', + 'response', + 'result', + 'retire', + 'retreat', + 'return', + 'reunion', + 'reveal', + 'review', + 'reward', + 'rhythm', + 'rib', + 'ribbon', + 'rice', + 'rich', + 'ride', + 'ridge', + 'rifle', + 'right', + 'rigid', + 'ring', + 'riot', + 'ripple', + 'risk', + 'ritual', + 'rival', + 'river', + 'road', + 'roast', + 'robot', + 'robust', + 'rocket', + 'romance', + 'roof', + 'rookie', + 'room', + 'rose', + 'rotate', + 'rough', + 'round', + 'route', + 'royal', + 'rubber', + 'rude', + 'rug', + 'rule', + 'run', + 'runway', + 'rural', + 'sad', + 'saddle', + 'sadness', + 'safe', + 'sail', + 'salad', + 'salmon', + 'salon', + 'salt', + 'salute', + 'same', + 'sample', + 'sand', + 'satisfy', + 'satoshi', + 'sauce', + 'sausage', + 'save', + 'say', + 'scale', + 'scan', + 'scare', + 'scatter', + 'scene', + 'scheme', + 'school', + 'science', + 'scissors', + 'scorpion', + 'scout', + 'scrap', + 'screen', + 'script', + 'scrub', + 'sea', + 'search', + 'season', + 'seat', + 'second', + 'secret', + 'section', + 'security', + 'seed', + 'seek', + 'segment', + 'select', + 'sell', + 'seminar', + 'senior', + 'sense', + 'sentence', + 'series', + 'service', + 'session', + 'settle', + 'setup', + 'seven', + 'shadow', + 'shaft', + 'shallow', + 'share', + 'shed', + 'shell', + 'sheriff', + 'shield', + 'shift', + 'shine', + 'ship', + 'shiver', + 'shock', + 'shoe', + 'shoot', + 'shop', + 'short', + 'shoulder', + 'shove', + 'shrimp', + 'shrug', + 'shuffle', + 'shy', + 'sibling', + 'sick', + 'side', + 'siege', + 'sight', + 'sign', + 'silent', + 'silk', + 'silly', + 'silver', + 'similar', + 'simple', + 'since', + 'sing', + 'siren', + 'sister', + 'situate', + 'six', + 'size', + 'skate', + 'sketch', + 'ski', + 'skill', + 'skin', + 'skirt', + 'skull', + 'slab', + 'slam', + 'sleep', + 'slender', + 'slice', + 'slide', + 'slight', + 'slim', + 'slogan', + 'slot', + 'slow', + 'slush', + 'small', + 'smart', + 'smile', + 'smoke', + 'smooth', + 'snack', + 'snake', + 'snap', + 'sniff', + 'snow', + 'soap', + 'soccer', + 'social', + 'sock', + 'soda', + 'soft', + 'solar', + 'soldier', + 'solid', + 'solution', + 'solve', + 'someone', + 'song', + 'soon', + 'sorry', + 'sort', + 'soul', + 'sound', + 'soup', + 'source', + 'south', + 'space', + 'spare', + 'spatial', + 'spawn', + 'speak', + 'special', + 'speed', + 'spell', + 'spend', + 'sphere', + 'spice', + 'spider', + 'spike', + 'spin', + 'spirit', + 'split', + 'spoil', + 'sponsor', + 'spoon', + 'sport', + 'spot', + 'spray', + 'spread', + 'spring', + 'spy', + 'square', + 'squeeze', + 'squirrel', + 'stable', + 'stadium', + 'staff', + 'stage', + 'stairs', + 'stamp', + 'stand', + 'start', + 'state', + 'stay', + 'steak', + 'steel', + 'stem', + 'step', + 'stereo', + 'stick', + 'still', + 'sting', + 'stock', + 'stomach', + 'stone', + 'stool', + 'story', + 'stove', + 'strategy', + 'street', + 'strike', + 'strong', + 'struggle', + 'student', + 'stuff', + 'stumble', + 'style', + 'subject', + 'submit', + 'subway', + 'success', + 'such', + 'sudden', + 'suffer', + 'sugar', + 'suggest', + 'suit', + 'summer', + 'sun', + 'sunny', + 'sunset', + 'super', + 'supply', + 'supreme', + 'sure', + 'surface', + 'surge', + 'surprise', + 'surround', + 'survey', + 'suspect', + 'sustain', + 'swallow', + 'swamp', + 'swap', + 'swarm', + 'swear', + 'sweet', + 'swift', + 'swim', + 'swing', + 'switch', + 'sword', + 'symbol', + 'symptom', + 'syrup', + 'system', + 'table', + 'tackle', + 'tag', + 'tail', + 'talent', + 'talk', + 'tank', + 'tape', + 'target', + 'task', + 'taste', + 'tattoo', + 'taxi', + 'teach', + 'team', + 'tell', + 'ten', + 'tenant', + 'tennis', + 'tent', + 'term', + 'test', + 'text', + 'thank', + 'that', + 'theme', + 'then', + 'theory', + 'there', + 'they', + 'thing', + 'this', + 'thought', + 'three', + 'thrive', + 'throw', + 'thumb', + 'thunder', + 'ticket', + 'tide', + 'tiger', + 'tilt', + 'timber', + 'time', + 'tiny', + 'tip', + 'tired', + 'tissue', + 'title', + 'toast', + 'tobacco', + 'today', + 'toddler', + 'toe', + 'together', + 'toilet', + 'token', + 'tomato', + 'tomorrow', + 'tone', + 'tongue', + 'tonight', + 'tool', + 'tooth', + 'top', + 'topic', + 'topple', + 'torch', + 'tornado', + 'tortoise', + 'toss', + 'total', + 'tourist', + 'toward', + 'tower', + 'town', + 'toy', + 'track', + 'trade', + 'traffic', + 'tragic', + 'train', + 'transfer', + 'trap', + 'trash', + 'travel', + 'tray', + 'treat', + 'tree', + 'trend', + 'trial', + 'tribe', + 'trick', + 'trigger', + 'trim', + 'trip', + 'trophy', + 'trouble', + 'truck', + 'true', + 'truly', + 'trumpet', + 'trust', + 'truth', + 'try', + 'tube', + 'tuition', + 'tumble', + 'tuna', + 'tunnel', + 'turkey', + 'turn', + 'turtle', + 'twelve', + 'twenty', + 'twice', + 'twin', + 'twist', + 'two', + 'type', + 'typical', + 'ugly', + 'umbrella', + 'unable', + 'unaware', + 'uncle', + 'uncover', + 'under', + 'undo', + 'unfair', + 'unfold', + 'unhappy', + 'uniform', + 'unique', + 'unit', + 'universe', + 'unknown', + 'unlock', + 'until', + 'unusual', + 'unveil', + 'update', + 'upgrade', + 'uphold', + 'upon', + 'upper', + 'upset', + 'urban', + 'urge', + 'usage', + 'use', + 'used', + 'useful', + 'useless', + 'usual', + 'utility', + 'vacant', + 'vacuum', + 'vague', + 'valid', + 'valley', + 'valve', + 'van', + 'vanish', + 'vapor', + 'various', + 'vast', + 'vault', + 'vehicle', + 'velvet', + 'vendor', + 'venture', + 'venue', + 'verb', + 'verify', + 'version', + 'very', + 'vessel', + 'veteran', + 'viable', + 'vibrant', + 'vicious', + 'victory', + 'video', + 'view', + 'village', + 'vintage', + 'violin', + 'virtual', + 'virus', + 'visa', + 'visit', + 'visual', + 'vital', + 'vivid', + 'vocal', + 'voice', + 'void', + 'volcano', + 'volume', + 'vote', + 'voyage', + 'wage', + 'wagon', + 'wait', + 'walk', + 'wall', + 'walnut', + 'want', + 'warfare', + 'warm', + 'warrior', + 'wash', + 'wasp', + 'waste', + 'water', + 'wave', + 'way', + 'wealth', + 'weapon', + 'wear', + 'weasel', + 'weather', + 'web', + 'wedding', + 'weekend', + 'weird', + 'welcome', + 'west', + 'wet', + 'whale', + 'what', + 'wheat', + 'wheel', + 'when', + 'where', + 'whip', + 'whisper', + 'wide', + 'width', + 'wife', + 'wild', + 'will', + 'win', + 'window', + 'wine', + 'wing', + 'wink', + 'winner', + 'winter', + 'wire', + 'wisdom', + 'wise', + 'wish', + 'witness', + 'wolf', + 'woman', + 'wonder', + 'wood', + 'wool', + 'word', + 'work', + 'world', + 'worry', + 'worth', + 'wrap', + 'wreck', + 'wrestle', + 'wrist', + 'write', + 'wrong', + 'yard', + 'year', + 'yellow', + 'you', + 'young', + 'youth', + 'zebra', + 'zero', + 'zone', + 'zoo', +]; + +export default english; diff --git a/src/multisig.ts b/src/multisig.ts new file mode 100644 index 0000000..7bf821d --- /dev/null +++ b/src/multisig.ts @@ -0,0 +1,496 @@ +import { Buffer } from 'buffer'; +import * as nacl from './nacl/naclWrappers'; +import * as address from './encoding/address'; +import * as encoding from './encoding/encoding'; +import * as txnBuilder from './transaction'; +import * as utils from './utils/utils'; +import AnyTransaction, { EncodedTransaction } from './types/transactions'; +import { MultisigMetadata } from './types/multisig'; +import { + EncodedMultisig, + EncodedSignedTransaction, +} from './types/transactions/encoded'; + +/** + Utilities for manipulating multisig transaction blobs. + */ + +export const MULTISIG_MERGE_LESSTHANTWO_ERROR_MSG = + 'Not enough multisig transactions to merge. Need at least two'; +export const MULTISIG_MERGE_MISMATCH_ERROR_MSG = + 'Cannot merge txs. txIDs differ'; +export const MULTISIG_MERGE_MISMATCH_AUTH_ADDR_MSG = + 'Cannot merge txs. Auth addrs differ'; +export const MULTISIG_MERGE_WRONG_PREIMAGE_ERROR_MSG = + 'Cannot merge txs. Multisig preimages differ'; +export const MULTISIG_MERGE_SIG_MISMATCH_ERROR_MSG = + 'Cannot merge txs. subsigs are mismatched.'; +const MULTISIG_KEY_NOT_EXIST_ERROR_MSG = 'Key does not exist'; +export const MULTISIG_NO_MUTATE_ERROR_MSG = + 'Cannot mutate a multisig field as it would invalidate all existing signatures.'; +export const MULTISIG_USE_PARTIAL_SIGN_ERROR_MSG = + 'Cannot sign a multisig transaction using `signTxn`. Use `partialSignTxn` instead.'; +export const MULTISIG_SIGNATURE_LENGTH_ERROR_MSG = + 'Cannot add multisig signature. Signature is not of the correct length.'; + +interface MultisigOptions { + rawSig: Uint8Array; + myPk: Uint8Array; +} + +interface MultisigMetadataWithPks extends Omit { + pks: Uint8Array[]; +} + +/** + * createMultisigTransaction creates a raw, unsigned multisig transaction blob. + * @param txn - the actual transaction. + * @param version - multisig version + * @param threshold - multisig threshold + * @param pks - ordered list of public keys in this multisig + * @returns encoded multisig blob + */ +export function createMultisigTransaction( + txn: txnBuilder.Transaction, + { version, threshold, addrs }: MultisigMetadata +) { + // construct the appendable multisigned transaction format + const pks = addrs.map((addr) => address.decodeAddress(addr).publicKey); + const subsigs = pks.map((pk) => ({ pk: Buffer.from(pk) })); + + const msig: EncodedMultisig = { + v: version, + thr: threshold, + subsig: subsigs, + }; + const txnForEncoding = txn.get_obj_for_encoding(); + const signedTxn: EncodedSignedTransaction = { + msig, + txn: txnForEncoding, + }; + + // if the address of this multisig is different from the transaction sender, + // we need to add the auth-addr field + const msigAddr = address.fromMultisigPreImg({ + version, + threshold, + pks, + }); + if ( + address.encodeAddress(txnForEncoding.snd) !== + address.encodeAddress(msigAddr) + ) { + signedTxn.sgnr = Buffer.from(msigAddr); + } + + return new Uint8Array(encoding.encode(signedTxn)); +} + +/** + * createMultisigTransactionWithSignature creates a multisig transaction blob with an included signature. + * @param txn - the actual transaction to sign. + * @param rawSig - a Buffer raw signature of that transaction + * @param myPk - a public key that corresponds with rawSig + * @param version - multisig version + * @param threshold - multisig threshold + * @param pks - ordered list of public keys in this multisig + * @returns encoded multisig blob + */ +function createMultisigTransactionWithSignature( + txn: txnBuilder.Transaction, + { rawSig, myPk }: MultisigOptions, + { version, threshold, pks }: MultisigMetadataWithPks +) { + // Create an empty encoded multisig transaction + const encodedMsig = createMultisigTransaction(txn, { + version, + threshold, + addrs: pks.map((pk) => address.encodeAddress(pk)), + }); + // note: this is not signed yet, but will be shortly + const signedTxn = encoding.decode(encodedMsig) as EncodedSignedTransaction; + + let keyExist = false; + // append the multisig signature to the corresponding public key in the multisig blob + signedTxn.msig.subsig.forEach((subsig, i) => { + if (nacl.bytesEqual(subsig.pk, myPk)) { + keyExist = true; + signedTxn.msig.subsig[i].s = rawSig; + } + }); + if (keyExist === false) { + throw new Error(MULTISIG_KEY_NOT_EXIST_ERROR_MSG); + } + + // if the address of this multisig is different from the transaction sender, + // we need to add the auth-addr field + const msigAddr = address.fromMultisigPreImg({ + version, + threshold, + pks, + }); + if ( + address.encodeAddress(signedTxn.txn.snd) !== address.encodeAddress(msigAddr) + ) { + signedTxn.sgnr = Buffer.from(msigAddr); + } + + return new Uint8Array(encoding.encode(signedTxn)); +} + +/** + * MultisigTransaction is a Transaction that also supports creating partially-signed multisig transactions. + */ +export class MultisigTransaction extends txnBuilder.Transaction { + /* eslint-disable class-methods-use-this,@typescript-eslint/no-unused-vars,no-dupe-class-members */ + /** + * Override inherited method to throw an error, as mutating transactions are prohibited in this context + */ + addLease() { + throw new Error(MULTISIG_NO_MUTATE_ERROR_MSG); + } + + /** + * Override inherited method to throw an error, as mutating transactions are prohibited in this context + */ + addRekey() { + throw new Error(MULTISIG_NO_MUTATE_ERROR_MSG); + } + + /** + * Override inherited method to throw an error, as traditional signing is not allowed + */ + signTxn(sk: Uint8Array): Uint8Array; // This overload ensures that the override has a compatible type definition with the parent method + signTxn(sk: any): any { + throw new Error(MULTISIG_USE_PARTIAL_SIGN_ERROR_MSG); + } + /* eslint-enable class-methods-use-this,@typescript-eslint/no-unused-vars,no-dupe-class-members */ + + /** + * partialSignTxn partially signs this transaction and returns a partially-signed multisig transaction, + * encoded with msgpack as a typed array. + * @param version - multisig version + * @param threshold - multisig threshold + * @param pks - multisig public key list, order is important. + * @param sk - an Algorand secret key to sign with. + * @returns an encoded, partially signed multisig transaction. + */ + partialSignTxn( + { version, threshold, pks }: MultisigMetadataWithPks, + sk: Uint8Array + ) { + // get signature verifier + const myPk = nacl.keyPairFromSecretKey(sk).publicKey; + return createMultisigTransactionWithSignature( + this, + { rawSig: this.rawSignTxn(sk), myPk }, + { version, threshold, pks } + ); + } + + /** + * partialSignWithMultisigSignature partially signs this transaction with an external raw multisig signature and returns + * a partially-signed multisig transaction, encoded with msgpack as a typed array. + * @param metadata - multisig metadata + * @param signerAddr - address of the signer + * @param signature - raw multisig signature + * @returns an encoded, partially signed multisig transaction. + */ + partialSignWithMultisigSignature( + metadata: MultisigMetadataWithPks, + signerAddr: string, + signature: Uint8Array + ) { + if (!nacl.isValidSignatureLength(signature.length)) { + throw new Error(MULTISIG_SIGNATURE_LENGTH_ERROR_MSG); + } + return createMultisigTransactionWithSignature( + this, + { + rawSig: signature, + myPk: address.decodeAddress(signerAddr).publicKey, + }, + metadata + ); + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + txnForEnc: EncodedTransaction + ): MultisigTransaction { + return super.from_obj_for_encoding(txnForEnc) as MultisigTransaction; + } +} + +/** + * mergeMultisigTransactions takes a list of multisig transaction blobs, and merges them. + * @param multisigTxnBlobs - a list of blobs representing encoded multisig txns + * @returns typed array msg-pack encoded multisig txn + */ +export function mergeMultisigTransactions(multisigTxnBlobs: Uint8Array[]) { + if (multisigTxnBlobs.length < 2) { + throw new Error(MULTISIG_MERGE_LESSTHANTWO_ERROR_MSG); + } + const refSigTx = encoding.decode( + multisigTxnBlobs[0] + ) as EncodedSignedTransaction; + const refTxID = MultisigTransaction.from_obj_for_encoding( + refSigTx.txn + ).txID(); + const refAuthAddr = refSigTx.sgnr + ? address.encodeAddress(refSigTx.sgnr) + : undefined; + const refPreImage = { + version: refSigTx.msig.v, + threshold: refSigTx.msig.thr, + pks: refSigTx.msig.subsig.map((subsig) => subsig.pk), + }; + const refMsigAddr = address.encodeAddress( + address.fromMultisigPreImg(refPreImage) + ); + + const newSubsigs = refSigTx.msig.subsig.map((sig) => ({ ...sig })); + for (let i = 1; i < multisigTxnBlobs.length; i++) { + const unisig = encoding.decode( + multisigTxnBlobs[i] + ) as EncodedSignedTransaction; + + const unisigAlgoTxn = MultisigTransaction.from_obj_for_encoding(unisig.txn); + if (unisigAlgoTxn.txID() !== refTxID) { + throw new Error(MULTISIG_MERGE_MISMATCH_ERROR_MSG); + } + + const authAddr = unisig.sgnr + ? address.encodeAddress(unisig.sgnr) + : undefined; + if (refAuthAddr !== authAddr) { + throw new Error(MULTISIG_MERGE_MISMATCH_AUTH_ADDR_MSG); + } + + // check multisig has same preimage as reference + if (unisig.msig.subsig.length !== refSigTx.msig.subsig.length) { + throw new Error(MULTISIG_MERGE_WRONG_PREIMAGE_ERROR_MSG); + } + const preimg: MultisigMetadataWithPks = { + version: unisig.msig.v, + threshold: unisig.msig.thr, + pks: unisig.msig.subsig.map((subsig) => subsig.pk), + }; + const msgigAddr = address.encodeAddress(address.fromMultisigPreImg(preimg)); + if (refMsigAddr !== msgigAddr) { + throw new Error(MULTISIG_MERGE_WRONG_PREIMAGE_ERROR_MSG); + } + + // now, we can merge + unisig.msig.subsig.forEach((uniSubsig, index) => { + if (!uniSubsig.s) return; + const current = newSubsigs[index]; + // we convert the Uint8Arrays uniSubsig.s and current.s to Buffers here because (as + // of Dec 2020) React overrides the buffer package with an older version that does + // not support Uint8Arrays in the comparison function. See this thread for more + // info: https://github.com/algorand/js-algorand-sdk/issues/252 + if ( + current.s && + Buffer.compare(Buffer.from(uniSubsig.s), Buffer.from(current.s)) !== 0 + ) { + // mismatch + throw new Error(MULTISIG_MERGE_SIG_MISMATCH_ERROR_MSG); + } + current.s = uniSubsig.s; + }); + } + const msig: EncodedMultisig = { + v: refSigTx.msig.v, + thr: refSigTx.msig.thr, + subsig: newSubsigs, + }; + const signedTxn: EncodedSignedTransaction = { + msig, + txn: refSigTx.txn, + }; + if (typeof refAuthAddr !== 'undefined') { + signedTxn.sgnr = Buffer.from(address.decodeAddress(refAuthAddr).publicKey); + } + return new Uint8Array(encoding.encode(signedTxn)); +} + +export function verifyMultisig( + toBeVerified: Uint8Array, + msig: EncodedMultisig, + publicKey: Uint8Array +) { + const version = msig.v; + const threshold = msig.thr; + const subsigs = msig.subsig; + + const pks = subsigs.map((subsig) => subsig.pk); + if (msig.subsig.length < threshold) { + return false; + } + + let pk: Uint8Array; + try { + pk = address.fromMultisigPreImg({ version, threshold, pks }); + } catch (e) { + return false; + } + + if (!utils.arrayEqual(pk, publicKey)) { + return false; + } + + let counter = 0; + for (const subsig of subsigs) { + if (subsig.s !== undefined) { + counter += 1; + } + } + if (counter < threshold) { + return false; + } + + let verifiedCounter = 0; + for (const subsig of subsigs) { + if (subsig.s !== undefined) { + if (nacl.verify(toBeVerified, subsig.s, subsig.pk)) { + verifiedCounter += 1; + } + } + } + + if (verifiedCounter < threshold) { + return false; + } + + return true; +} + +/** + * signMultisigTransaction takes a raw transaction (see signTransaction), a multisig preimage, a secret key, and returns + * a multisig transaction, which is a blob representing a transaction and multisignature account preimage. The returned + * multisig txn can accumulate additional signatures through mergeMultisigTransactions or appendSignMultisigTransaction. + * @param txn - object with either payment or key registration fields + * @param version - multisig version + * @param threshold - multisig threshold + * @param addrs - a list of Algorand addresses representing possible signers for this multisig. Order is important. + * @param sk - Algorand secret key. The corresponding pk should be in the pre image. + * @returns object containing txID, and blob of partially signed multisig transaction (with multisig preimage information) + * If the final calculated fee is lower than the protocol minimum fee, the fee will be increased to match the minimum. + */ +export function signMultisigTransaction( + txn: txnBuilder.TransactionLike, + { version, threshold, addrs }: MultisigMetadata, + sk: Uint8Array +) { + // check that the from field matches the mSigPreImage. If from field is not populated, fill it in. + const expectedFromRaw = address.fromMultisigPreImgAddrs({ + version, + threshold, + addrs, + }); + if (!Object.prototype.hasOwnProperty.call(txn, 'from')) { + // eslint-disable-next-line no-param-reassign + txn.from = expectedFromRaw; + } + // build pks for partialSign + const pks = addrs.map((addr) => address.decodeAddress(addr).publicKey); + // `txn` needs to be handled differently if it's a constructed `Transaction` vs a dict of constructor args + const txnAlreadyBuilt = txn instanceof txnBuilder.Transaction; + let algoTxn: MultisigTransaction; + let blob: Uint8Array; + if (txnAlreadyBuilt) { + algoTxn = (txn as unknown) as MultisigTransaction; + blob = MultisigTransaction.prototype.partialSignTxn.call( + algoTxn, + { version, threshold, pks }, + sk + ); + } else { + algoTxn = new MultisigTransaction(txn as AnyTransaction); + blob = algoTxn.partialSignTxn({ version, threshold, pks }, sk); + } + return { + txID: algoTxn.txID().toString(), + blob, + }; +} + +/** + * appendSignMultisigTransaction takes a multisig transaction blob, and appends our signature to it. + * While we could derive public key preimagery from the partially-signed multisig transaction, + * we ask the caller to pass it back in, to ensure they know what they are signing. + * @param multisigTxnBlob - an encoded multisig txn. Supports non-payment txn types. + * @param version - multisig version + * @param threshold - multisig threshold + * @param addrs - a list of Algorand addresses representing possible signers for this multisig. Order is important. + * @param sk - Algorand secret key + * @returns object containing txID, and blob representing encoded multisig txn + */ +export function appendSignMultisigTransaction( + multisigTxnBlob: Uint8Array, + { version, threshold, addrs }: MultisigMetadata, + sk: Uint8Array +) { + const pks = addrs.map((addr) => address.decodeAddress(addr).publicKey); + // obtain underlying txn, sign it, and merge it + const multisigTxObj = encoding.decode( + multisigTxnBlob + ) as EncodedSignedTransaction; + const msigTxn = MultisigTransaction.from_obj_for_encoding(multisigTxObj.txn); + const partialSignedBlob = msigTxn.partialSignTxn( + { version, threshold, pks }, + sk + ); + return { + txID: msigTxn.txID().toString(), + blob: mergeMultisigTransactions([multisigTxnBlob, partialSignedBlob]), + }; +} + +/** + * appendMultisigTransactionSignature takes a multisig transaction blob, and appends a given raw signature to it. + * This makes it possible to compile a multisig signature using only raw signatures from external methods. + * @param multisigTxnBlob - an encoded multisig txn. Supports non-payment txn types. + * @param version - multisig version + * @param threshold - multisig threshold + * @param addrs - a list of Algorand addresses representing possible signers for this multisig. Order is important. + * @param signerAddr - address of the signer + * @param signature - raw multisig signature + * @returns object containing txID, and blob representing encoded multisig txn + */ +export function appendSignRawMultisigSignature( + multisigTxnBlob: Uint8Array, + { version, threshold, addrs }: MultisigMetadata, + signerAddr: string, + signature: Uint8Array +) { + const pks = addrs.map((addr) => address.decodeAddress(addr).publicKey); + // obtain underlying txn, sign it, and merge it + const multisigTxObj = encoding.decode( + multisigTxnBlob + ) as EncodedSignedTransaction; + const msigTxn = MultisigTransaction.from_obj_for_encoding(multisigTxObj.txn); + const partialSignedBlob = msigTxn.partialSignWithMultisigSignature( + { version, threshold, pks }, + signerAddr, + signature + ); + return { + txID: msigTxn.txID().toString(), + blob: mergeMultisigTransactions([multisigTxnBlob, partialSignedBlob]), + }; +} + +/** + * multisigAddress takes multisig metadata (preimage) and returns the corresponding human readable Algorand address. + * @param version - multisig version + * @param threshold - multisig threshold + * @param addrs - list of Algorand addresses + */ +export function multisigAddress({ + version, + threshold, + addrs, +}: MultisigMetadata) { + return address.fromMultisigPreImgAddrs({ version, threshold, addrs }); +} diff --git a/src/nacl/naclWrappers.ts b/src/nacl/naclWrappers.ts new file mode 100644 index 0000000..91027ee --- /dev/null +++ b/src/nacl/naclWrappers.ts @@ -0,0 +1,49 @@ +import nacl from 'tweetnacl'; +import sha512 from 'js-sha512'; + +export function genericHash(arr: sha512.Message) { + return sha512.sha512_256.array(arr); +} + +export function randomBytes(length: number) { + return nacl.randomBytes(length); +} + +export function keyPairFromSeed(seed: Uint8Array) { + return nacl.sign.keyPair.fromSeed(seed); +} + +export function keyPair() { + const seed = randomBytes(nacl.box.secretKeyLength); + return keyPairFromSeed(seed); +} + +export function isValidSignatureLength(len: number) { + return len === nacl.sign.signatureLength; +} + +export function keyPairFromSecretKey(sk: Uint8Array) { + return nacl.sign.keyPair.fromSecretKey(sk); +} + +export function sign(msg: Uint8Array, secretKey: Uint8Array) { + return nacl.sign.detached(msg, secretKey); +} + +export function bytesEqual(a: Uint8Array, b: Uint8Array) { + return nacl.verify(a, b); +} + +export function verify( + message: Uint8Array, + signature: Uint8Array, + verifyKey: Uint8Array +) { + return nacl.sign.detached.verify(message, signature, verifyKey); +} + +// constants +export const PUBLIC_KEY_LENGTH = nacl.sign.publicKeyLength; +export const SECRET_KEY_LENGTH = nacl.sign.secretKeyLength; +export const HASH_BYTES_LENGTH = 32; +export const SEED_BTYES_LENGTH = 32; diff --git a/src/signer.ts b/src/signer.ts new file mode 100644 index 0000000..4e9ef16 --- /dev/null +++ b/src/signer.ts @@ -0,0 +1,122 @@ +import { encodeUnsignedSimulateTransaction, Transaction } from './transaction'; +import Account from './types/account'; +import { LogicSigAccount, signLogicSigTransactionObject } from './logicsig'; +import { MultisigMetadata } from './types/multisig'; +import { signMultisigTransaction, mergeMultisigTransactions } from './multisig'; + +/** + * This type represents a function which can sign transactions from an atomic transaction group. + * @param txnGroup - The atomic group containing transactions to be signed + * @param indexesToSign - An array of indexes in the atomic transaction group that should be signed + * @returns A promise which resolves an array of encoded signed transactions. The length of the + * array will be the same as the length of indexesToSign, and each index i in the array + * corresponds to the signed transaction from txnGroup[indexesToSign[i]] + */ +export type TransactionSigner = ( + txnGroup: Transaction[], + indexesToSign: number[] +) => Promise; + +/** + * Create a TransactionSigner that can sign transactions for the provided basic Account. + */ +export function makeBasicAccountTransactionSigner( + account: Account +): TransactionSigner { + return (txnGroup: Transaction[], indexesToSign: number[]) => { + const signed: Uint8Array[] = []; + + for (const index of indexesToSign) { + signed.push(txnGroup[index].signTxn(account.sk)); + } + + return Promise.resolve(signed); + }; +} + +/** + * Create a TransactionSigner that can sign transactions for the provided LogicSigAccount. + */ +export function makeLogicSigAccountTransactionSigner( + account: LogicSigAccount +): TransactionSigner { + return (txnGroup: Transaction[], indexesToSign: number[]) => { + const signed: Uint8Array[] = []; + + for (const index of indexesToSign) { + const { blob } = signLogicSigTransactionObject(txnGroup[index], account); + signed.push(blob); + } + + return Promise.resolve(signed); + }; +} + +/** + * Create a TransactionSigner that can sign transactions for the provided Multisig account. + * @param msig - The Multisig account metadata + * @param sks - An array of private keys belonging to the msig which should sign the transactions. + */ +export function makeMultiSigAccountTransactionSigner( + msig: MultisigMetadata, + sks: Uint8Array[] +): TransactionSigner { + return (txnGroup: Transaction[], indexesToSign: number[]) => { + const signed: Uint8Array[] = []; + + for (const index of indexesToSign) { + const txn = txnGroup[index]; + const partialSigs: Uint8Array[] = []; + + for (const sk of sks) { + const { blob } = signMultisigTransaction(txn, msig, sk); + partialSigs.push(blob); + } + + signed.push(mergeMultisigTransactions(partialSigs)); + } + + return Promise.resolve(signed); + }; +} + +/** + * Create a makeEmptyTransactionSigner that does not specify any signer or + * signing capabilities. This should only be used to simulate transactions. + */ +export function makeEmptyTransactionSigner(): TransactionSigner { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + return (txnGroup: Transaction[], indexesToSign: number[]) => { + const unsigned: Uint8Array[] = []; + + for (const index of indexesToSign) { + unsigned.push(encodeUnsignedSimulateTransaction(txnGroup[index])); + } + + return Promise.resolve(unsigned); + }; +} + +/** Represents an unsigned transactions and a signer that can authorize that transaction. */ +export interface TransactionWithSigner { + /** An unsigned transaction */ + txn: Transaction; + /** A transaction signer that can authorize txn */ + signer: TransactionSigner; +} + +/** + * Check if a value conforms to the TransactionWithSigner structure. + * @param value - The value to check. + * @returns True if an only if the value has the structure of a TransactionWithSigner. + */ +export function isTransactionWithSigner( + value: any +): value is TransactionWithSigner { + return ( + typeof value === 'object' && + Object.keys(value).length === 2 && + typeof value.txn === 'object' && + typeof value.signer === 'function' + ); +} diff --git a/src/transaction.ts b/src/transaction.ts new file mode 100644 index 0000000..884de26 --- /dev/null +++ b/src/transaction.ts @@ -0,0 +1,1381 @@ +import { Buffer } from 'buffer'; +import base32 from 'hi-base32'; +import * as address from './encoding/address'; +import * as encoding from './encoding/encoding'; +import * as nacl from './nacl/naclWrappers'; +import * as utils from './utils/utils'; +import { translateBoxReferences } from './boxStorage'; +import { + OnApplicationComplete, + TransactionParams, + TransactionType, + isTransactionType, + BoxReference, +} from './types/transactions/base'; +import AnyTransaction, { + MustHaveSuggestedParams, + MustHaveSuggestedParamsInline, + EncodedTransaction, + EncodedSignedTransaction, + EncodedMultisig, + EncodedLogicSig, +} from './types/transactions'; +import { Address } from './types/address'; + +const ALGORAND_TRANSACTION_LENGTH = 52; +export const ALGORAND_MIN_TX_FEE = 1000; // version v5 +const ALGORAND_TRANSACTION_LEASE_LENGTH = 32; +const ALGORAND_MAX_ASSET_DECIMALS = 19; +const NUM_ADDL_BYTES_AFTER_SIGNING = 75; // NUM_ADDL_BYTES_AFTER_SIGNING is the number of bytes added to a txn after signing it +const ALGORAND_TRANSACTION_LEASE_LABEL_LENGTH = 5; +const ALGORAND_TRANSACTION_ADDRESS_LENGTH = 32; +const ALGORAND_TRANSACTION_REKEY_LABEL_LENGTH = 5; +const ASSET_METADATA_HASH_LENGTH = 32; +const KEYREG_VOTE_KEY_LENGTH = 32; +const KEYREG_SELECTION_KEY_LENGTH = 32; +const KEYREG_STATE_PROOF_KEY_LENGTH = 64; + +type AnyTransactionWithParams = MustHaveSuggestedParams; +type AnyTransactionWithParamsInline = MustHaveSuggestedParamsInline; + +/** + * A modified version of the transaction params. Represents the internal structure that the Transaction class uses + * to store inputted transaction objects. + */ +// Omit allows overwriting properties +interface TransactionStorageStructure + extends Omit< + TransactionParams, + | 'from' + | 'to' + | 'genesisHash' + | 'closeRemainderTo' + | 'voteKey' + | 'selectionKey' + | 'stateProofKey' + | 'assetManager' + | 'assetReserve' + | 'assetFreeze' + | 'assetClawback' + | 'assetRevocationTarget' + | 'freezeAccount' + | 'appAccounts' + | 'suggestedParams' + | 'reKeyTo' + > { + from: string | Address; + to: string | Address; + fee: number; + amount: number | bigint; + firstRound: number; + lastRound: number; + note?: Uint8Array; + genesisID: string; + genesisHash: string | Buffer; + lease?: Uint8Array; + closeRemainderTo?: string | Address; + voteKey: string | Buffer; + selectionKey: string | Buffer; + stateProofKey: string | Buffer; + voteFirst: number; + voteLast: number; + voteKeyDilution: number; + assetIndex: number; + assetTotal: number | bigint; + assetDecimals: number; + assetDefaultFrozen: boolean; + assetManager: string | Address; + assetReserve: string | Address; + assetFreeze: string | Address; + assetClawback: string | Address; + assetUnitName: string; + assetName: string; + assetURL: string; + assetMetadataHash?: string | Uint8Array; + freezeAccount: string | Address; + freezeState: boolean; + assetRevocationTarget?: string | Address; + appIndex: number; + appOnComplete: OnApplicationComplete; + appLocalInts: number; + appLocalByteSlices: number; + appGlobalInts: number; + appGlobalByteSlices: number; + appApprovalProgram: Uint8Array; + appClearProgram: Uint8Array; + appArgs?: Uint8Array[]; + appAccounts?: string[] | Address[]; + appForeignApps?: number[]; + appForeignAssets?: number[]; + type?: TransactionType; + flatFee: boolean; + reKeyTo?: string | Address; + nonParticipation?: boolean; + group?: Buffer; + extraPages?: number; + boxes?: BoxReference[]; + stateProofType?: number | bigint; + stateProof?: Uint8Array; + stateProofMessage?: Uint8Array; +} + +function getKeyregKey( + input: undefined | string | Uint8Array | Buffer, + inputName: string, + length: number +): Buffer | undefined { + if (input == null) { + return undefined; + } + + let inputAsBuffer: Buffer | undefined; + + if (typeof input === 'string') { + inputAsBuffer = Buffer.from(input, 'base64'); + } else if (input.constructor === Uint8Array) { + inputAsBuffer = Buffer.from(input); + } else if (Buffer.isBuffer(input)) { + inputAsBuffer = input; + } + + if (inputAsBuffer == null || inputAsBuffer.byteLength !== length) { + throw Error( + `${inputName} must be a ${length} byte Uint8Array or Buffer or base64 string.` + ); + } + + return inputAsBuffer; +} + +/** + * Transaction enables construction of Algorand transactions + * */ +export class Transaction implements TransactionStorageStructure { + name = 'Transaction'; + tag = Buffer.from('TX'); + + // Implement transaction params + from: Address; + to: Address; + fee: number; + amount: number | bigint; + firstRound: number; + lastRound: number; + note?: Uint8Array; + genesisID: string; + genesisHash: Buffer; + lease?: Uint8Array; + closeRemainderTo?: Address; + voteKey: Buffer; + selectionKey: Buffer; + stateProofKey: Buffer; + voteFirst: number; + voteLast: number; + voteKeyDilution: number; + assetIndex: number; + assetTotal: number | bigint; + assetDecimals: number; + assetDefaultFrozen: boolean; + assetManager: Address; + assetReserve: Address; + assetFreeze: Address; + assetClawback: Address; + assetUnitName: string; + assetName: string; + assetURL: string; + assetMetadataHash?: Uint8Array; + freezeAccount: Address; + freezeState: boolean; + assetRevocationTarget?: Address; + appIndex: number; + appOnComplete: OnApplicationComplete; + appLocalInts: number; + appLocalByteSlices: number; + appGlobalInts: number; + appGlobalByteSlices: number; + appApprovalProgram: Uint8Array; + appClearProgram: Uint8Array; + appArgs?: Uint8Array[]; + appAccounts?: Address[]; + appForeignApps?: number[]; + appForeignAssets?: number[]; + boxes?: BoxReference[]; + type?: TransactionType; + flatFee: boolean; + reKeyTo?: Address; + nonParticipation?: boolean; + group?: Buffer; + extraPages?: number; + stateProofType?: number | bigint; + stateProof?: Uint8Array; + stateProofMessage?: Uint8Array; + + constructor({ ...transaction }: AnyTransaction) { + // Populate defaults + /* eslint-disable no-param-reassign */ + const defaults: Partial = { + type: TransactionType.pay, + flatFee: false, + nonParticipation: false, + }; + // Default type + if (typeof transaction.type === 'undefined') { + transaction.type = defaults.type; + } + // Default flatFee + if ( + typeof (transaction as AnyTransactionWithParamsInline).flatFee === + 'undefined' + ) { + (transaction as AnyTransactionWithParamsInline).flatFee = + defaults.flatFee; + } + // Default nonParticipation + if ( + transaction.type === TransactionType.keyreg && + typeof transaction.voteKey !== 'undefined' && + typeof transaction.nonParticipation === 'undefined' + ) { + transaction.nonParticipation = defaults.nonParticipation; + } + /* eslint-enable no-param-reassign */ + + // Move suggested parameters from its object to inline + if ( + (transaction as AnyTransactionWithParams).suggestedParams !== undefined + ) { + // Create a temporary reference to the transaction object that has params inline and also as a suggested params object + // - Helpful for moving params from named object to inline + const reference = transaction as AnyTransactionWithParams & + AnyTransactionWithParamsInline; + reference.genesisHash = reference.suggestedParams.genesisHash; + reference.fee = reference.suggestedParams.fee; + if (reference.suggestedParams.flatFee !== undefined) + reference.flatFee = reference.suggestedParams.flatFee; + reference.firstRound = reference.suggestedParams.firstRound; + reference.lastRound = reference.suggestedParams.lastRound; + reference.genesisID = reference.suggestedParams.genesisID; + } + + // At this point all suggestedParams have been moved to be inline, so we can reassign the transaction object type + // to one which is more useful as we prepare properties for storing + const txn = transaction as TransactionStorageStructure; + + txn.from = address.decodeAddress(txn.from as string); + if (txn.to !== undefined) txn.to = address.decodeAddress(txn.to as string); + if (txn.closeRemainderTo !== undefined) + txn.closeRemainderTo = address.decodeAddress( + txn.closeRemainderTo as string + ); + if (txn.assetManager !== undefined) + txn.assetManager = address.decodeAddress(txn.assetManager as string); + if (txn.assetReserve !== undefined) + txn.assetReserve = address.decodeAddress(txn.assetReserve as string); + if (txn.assetFreeze !== undefined) + txn.assetFreeze = address.decodeAddress(txn.assetFreeze as string); + if (txn.assetClawback !== undefined) + txn.assetClawback = address.decodeAddress(txn.assetClawback as string); + if (txn.assetRevocationTarget !== undefined) + txn.assetRevocationTarget = address.decodeAddress( + txn.assetRevocationTarget as string + ); + if (txn.freezeAccount !== undefined) + txn.freezeAccount = address.decodeAddress(txn.freezeAccount as string); + if (txn.reKeyTo !== undefined) + txn.reKeyTo = address.decodeAddress(txn.reKeyTo as string); + if (txn.genesisHash === undefined) + throw Error('genesis hash must be specified and in a base64 string.'); + + txn.genesisHash = Buffer.from(txn.genesisHash as string, 'base64'); + + if ( + txn.amount !== undefined && + (!( + Number.isSafeInteger(txn.amount) || + (typeof txn.amount === 'bigint' && + txn.amount <= BigInt('0xffffffffffffffff')) + ) || + txn.amount < 0) + ) + throw Error( + 'Amount must be a positive number and smaller than 2^64-1. If the number is larger than 2^53-1, use bigint.' + ); + if (!Number.isSafeInteger(txn.fee) || txn.fee < 0) + throw Error('fee must be a positive number and smaller than 2^53-1'); + if (!Number.isSafeInteger(txn.firstRound) || txn.firstRound < 0) + throw Error('firstRound must be a positive number'); + if (!Number.isSafeInteger(txn.lastRound) || txn.lastRound < 0) + throw Error('lastRound must be a positive number'); + if ( + txn.extraPages !== undefined && + (!Number.isInteger(txn.extraPages) || + txn.extraPages < 0 || + txn.extraPages > 3) + ) + throw Error('extraPages must be an Integer between and including 0 to 3'); + if ( + txn.assetTotal !== undefined && + (!( + Number.isSafeInteger(txn.assetTotal) || + (typeof txn.assetTotal === 'bigint' && + txn.assetTotal <= BigInt('0xffffffffffffffff')) + ) || + txn.assetTotal < 0) + ) + throw Error( + 'Total asset issuance must be a positive number and smaller than 2^64-1. If the number is larger than 2^53-1, use bigint.' + ); + if ( + txn.assetDecimals !== undefined && + (!Number.isSafeInteger(txn.assetDecimals) || + txn.assetDecimals < 0 || + txn.assetDecimals > ALGORAND_MAX_ASSET_DECIMALS) + ) + throw Error( + `assetDecimals must be a positive number and smaller than ${ALGORAND_MAX_ASSET_DECIMALS.toString()}` + ); + if ( + txn.assetIndex !== undefined && + (!Number.isSafeInteger(txn.assetIndex) || txn.assetIndex < 0) + ) + throw Error( + 'Asset index must be a positive number and smaller than 2^53-1' + ); + if ( + txn.appIndex !== undefined && + (!Number.isSafeInteger(txn.appIndex) || txn.appIndex < 0) + ) + throw Error( + 'Application index must be a positive number and smaller than 2^53-1' + ); + if ( + txn.appLocalInts !== undefined && + (!Number.isSafeInteger(txn.appLocalInts) || txn.appLocalInts < 0) + ) + throw Error( + 'Application local ints count must be a positive number and smaller than 2^53-1' + ); + if ( + txn.appLocalByteSlices !== undefined && + (!Number.isSafeInteger(txn.appLocalByteSlices) || + txn.appLocalByteSlices < 0) + ) + throw Error( + 'Application local byte slices count must be a positive number and smaller than 2^53-1' + ); + if ( + txn.appGlobalInts !== undefined && + (!Number.isSafeInteger(txn.appGlobalInts) || txn.appGlobalInts < 0) + ) + throw Error( + 'Application global ints count must be a positive number and smaller than 2^53-1' + ); + if ( + txn.appGlobalByteSlices !== undefined && + (!Number.isSafeInteger(txn.appGlobalByteSlices) || + txn.appGlobalByteSlices < 0) + ) + throw Error( + 'Application global byte slices count must be a positive number and smaller than 2^53-1' + ); + if (txn.appApprovalProgram !== undefined) { + if (txn.appApprovalProgram.constructor !== Uint8Array) + throw Error('appApprovalProgram must be a Uint8Array.'); + } + if (txn.appClearProgram !== undefined) { + if (txn.appClearProgram.constructor !== Uint8Array) + throw Error('appClearProgram must be a Uint8Array.'); + } + if (txn.appArgs !== undefined) { + if (!Array.isArray(txn.appArgs)) + throw Error('appArgs must be an Array of Uint8Array.'); + txn.appArgs = txn.appArgs.slice(); + txn.appArgs.forEach((arg) => { + if (arg.constructor !== Uint8Array) + throw Error('each element of AppArgs must be a Uint8Array.'); + }); + } else { + txn.appArgs = []; + } + if (txn.appAccounts !== undefined) { + if (!Array.isArray(txn.appAccounts)) + throw Error('appAccounts must be an Array of addresses.'); + txn.appAccounts = txn.appAccounts.map((addressAsString) => + address.decodeAddress(addressAsString) + ); + } + if (txn.appForeignApps !== undefined) { + if (!Array.isArray(txn.appForeignApps)) + throw Error('appForeignApps must be an Array of integers.'); + txn.appForeignApps = txn.appForeignApps.slice(); + txn.appForeignApps.forEach((foreignAppIndex) => { + if (!Number.isSafeInteger(foreignAppIndex) || foreignAppIndex < 0) + throw Error( + 'each foreign application index must be a positive number and smaller than 2^53-1' + ); + }); + } + if (txn.appForeignAssets !== undefined) { + if (!Array.isArray(txn.appForeignAssets)) + throw Error('appForeignAssets must be an Array of integers.'); + txn.appForeignAssets = txn.appForeignAssets.slice(); + txn.appForeignAssets.forEach((foreignAssetIndex) => { + if (!Number.isSafeInteger(foreignAssetIndex) || foreignAssetIndex < 0) + throw Error( + 'each foreign asset index must be a positive number and smaller than 2^53-1' + ); + }); + } + if (txn.boxes !== undefined) { + if (!Array.isArray(txn.boxes)) + throw Error('boxes must be an Array of BoxReference.'); + txn.boxes = txn.boxes.slice(); + txn.boxes.forEach((box) => { + if ( + !Number.isSafeInteger(box.appIndex) || + box.name.constructor !== Uint8Array + ) + throw Error( + 'box app index must be a number and name must be an Uint8Array.' + ); + }); + } + if ( + txn.assetMetadataHash !== undefined && + txn.assetMetadataHash.length !== 0 + ) { + if (typeof txn.assetMetadataHash === 'string') { + txn.assetMetadataHash = new Uint8Array( + Buffer.from(txn.assetMetadataHash) + ); + } + + if ( + txn.assetMetadataHash.constructor !== Uint8Array || + txn.assetMetadataHash.byteLength !== ASSET_METADATA_HASH_LENGTH + ) { + throw Error( + `assetMetadataHash must be a ${ASSET_METADATA_HASH_LENGTH} byte Uint8Array or string.` + ); + } + + if (txn.assetMetadataHash.every((value) => value === 0)) { + // if hash contains all 0s, omit it + txn.assetMetadataHash = undefined; + } + } else { + txn.assetMetadataHash = undefined; + } + if (txn.note !== undefined) { + if (txn.note.constructor !== Uint8Array) + throw Error('note must be a Uint8Array.'); + } else { + txn.note = new Uint8Array(0); + } + if (txn.lease !== undefined) { + if (txn.lease.constructor !== Uint8Array) + throw Error('lease must be a Uint8Array.'); + if (txn.lease.length !== ALGORAND_TRANSACTION_LEASE_LENGTH) + throw Error( + `lease must be of length ${ALGORAND_TRANSACTION_LEASE_LENGTH.toString()}.` + ); + if (txn.lease.every((value) => value === 0)) { + // if lease contains all 0s, omit it + txn.lease = new Uint8Array(0); + } + } else { + txn.lease = new Uint8Array(0); + } + txn.voteKey = getKeyregKey(txn.voteKey, 'voteKey', KEYREG_VOTE_KEY_LENGTH); + txn.selectionKey = getKeyregKey( + txn.selectionKey, + 'selectionKey', + KEYREG_SELECTION_KEY_LENGTH + ); + txn.stateProofKey = getKeyregKey( + txn.stateProofKey, + 'stateProofKey', + KEYREG_STATE_PROOF_KEY_LENGTH + ); + // Checking non-participation key registration + if ( + txn.nonParticipation && + (txn.voteKey || + txn.selectionKey || + txn.voteFirst || + txn.stateProofKey || + txn.voteLast || + txn.voteKeyDilution) + ) { + throw new Error( + 'nonParticipation is true but participation params are present.' + ); + } + // Checking online key registration + if ( + !txn.nonParticipation && + (txn.voteKey || + txn.selectionKey || + txn.stateProofKey || + txn.voteFirst || + txn.voteLast || + txn.voteKeyDilution) && + !( + txn.voteKey && + txn.selectionKey && + txn.voteFirst && + txn.voteLast && + txn.voteKeyDilution + ) + // stateProofKey not included here for backwards compatibility + ) { + throw new Error( + 'online key registration missing at least one of the following fields: ' + + 'voteKey, selectionKey, voteFirst, voteLast, voteKeyDilution' + ); + } + // The last option is an offline key registration where all the fields + // nonParticipation, voteKey, selectionKey, voteFirst, voteLast, voteKeyDilution + // are all undefined/false + + // Remove unwanted properties and store transaction on instance + delete ((txn as unknown) as AnyTransactionWithParams).suggestedParams; + Object.assign(this, utils.removeUndefinedProperties(txn)); + + // Modify Fee + if (!txn.flatFee) { + this.fee *= this.estimateSize(); + // If suggested fee too small and will be rejected, set to min tx fee + if (this.fee < ALGORAND_MIN_TX_FEE) { + this.fee = ALGORAND_MIN_TX_FEE; + } + } + + // say we are aware of groups + this.group = undefined; + + // stpf fields + if ( + txn.stateProofType !== undefined && + (!Number.isSafeInteger(txn.stateProofType) || txn.stateProofType < 0) + ) + throw Error( + 'State Proof type must be a positive number and smaller than 2^53-1' + ); + if (txn.stateProofMessage !== undefined) { + if (txn.stateProofMessage.constructor !== Uint8Array) + throw Error('stateProofMessage must be a Uint8Array.'); + } else { + txn.stateProofMessage = new Uint8Array(0); + } + if (txn.stateProof !== undefined) { + if (txn.stateProof.constructor !== Uint8Array) + throw Error('stateProof must be a Uint8Array.'); + } else { + txn.stateProof = new Uint8Array(0); + } + } + + // eslint-disable-next-line camelcase + get_obj_for_encoding() { + if (this.type === 'pay') { + const txn: EncodedTransaction = { + amt: this.amount, + fee: this.fee, + fv: this.firstRound, + lv: this.lastRound, + note: Buffer.from(this.note), + snd: Buffer.from(this.from.publicKey), + type: 'pay', + gen: this.genesisID, + gh: this.genesisHash, + lx: Buffer.from(this.lease), + grp: this.group, + }; + + // parse close address + if ( + this.closeRemainderTo !== undefined && + address.encodeAddress(this.closeRemainderTo.publicKey) !== + address.ALGORAND_ZERO_ADDRESS_STRING + ) { + txn.close = Buffer.from(this.closeRemainderTo.publicKey); + } + if (this.reKeyTo !== undefined) { + txn.rekey = Buffer.from(this.reKeyTo.publicKey); + } + // allowed zero values + if (this.to !== undefined) txn.rcv = Buffer.from(this.to.publicKey); + if (!txn.note.length) delete txn.note; + if (!txn.amt) delete txn.amt; + if (!txn.fee) delete txn.fee; + if (!txn.fv) delete txn.fv; + if (!txn.gen) delete txn.gen; + if (txn.grp === undefined) delete txn.grp; + if (!txn.lx.length) delete txn.lx; + if (!txn.rekey) delete txn.rekey; + return txn; + } + if (this.type === 'keyreg') { + const txn: EncodedTransaction = { + fee: this.fee, + fv: this.firstRound, + lv: this.lastRound, + note: Buffer.from(this.note), + snd: Buffer.from(this.from.publicKey), + type: this.type, + gen: this.genesisID, + gh: this.genesisHash, + lx: Buffer.from(this.lease), + grp: this.group, + votekey: this.voteKey, + selkey: this.selectionKey, + sprfkey: this.stateProofKey, + votefst: this.voteFirst, + votelst: this.voteLast, + votekd: this.voteKeyDilution, + }; + // allowed zero values + if (!txn.note.length) delete txn.note; + if (!txn.lx.length) delete txn.lx; + if (!txn.fee) delete txn.fee; + if (!txn.fv) delete txn.fv; + if (!txn.gen) delete txn.gen; + if (txn.grp === undefined) delete txn.grp; + if (this.reKeyTo !== undefined) { + txn.rekey = Buffer.from(this.reKeyTo.publicKey); + } + if (this.nonParticipation) { + txn.nonpart = true; + } + if (!txn.selkey) delete txn.selkey; + if (!txn.votekey) delete txn.votekey; + if (!txn.sprfkey) delete txn.sprfkey; + if (!txn.votefst) delete txn.votefst; + if (!txn.votelst) delete txn.votelst; + if (!txn.votekd) delete txn.votekd; + return txn; + } + if (this.type === 'acfg') { + // asset creation, or asset reconfigure, or asset destruction + const txn: EncodedTransaction = { + fee: this.fee, + fv: this.firstRound, + lv: this.lastRound, + note: Buffer.from(this.note), + snd: Buffer.from(this.from.publicKey), + type: this.type, + gen: this.genesisID, + gh: this.genesisHash, + lx: Buffer.from(this.lease), + grp: this.group, + caid: this.assetIndex, + apar: { + t: this.assetTotal, + df: this.assetDefaultFrozen, + dc: this.assetDecimals, + }, + }; + if (this.assetManager !== undefined) + txn.apar.m = Buffer.from(this.assetManager.publicKey); + if (this.assetReserve !== undefined) + txn.apar.r = Buffer.from(this.assetReserve.publicKey); + if (this.assetFreeze !== undefined) + txn.apar.f = Buffer.from(this.assetFreeze.publicKey); + if (this.assetClawback !== undefined) + txn.apar.c = Buffer.from(this.assetClawback.publicKey); + if (this.assetName !== undefined) txn.apar.an = this.assetName; + if (this.assetUnitName !== undefined) txn.apar.un = this.assetUnitName; + if (this.assetURL !== undefined) txn.apar.au = this.assetURL; + if (this.assetMetadataHash !== undefined) + txn.apar.am = Buffer.from(this.assetMetadataHash); + + // allowed zero values + if (!txn.note.length) delete txn.note; + if (!txn.lx.length) delete txn.lx; + if (!txn.amt) delete txn.amt; + if (!txn.fee) delete txn.fee; + if (!txn.fv) delete txn.fv; + if (!txn.gen) delete txn.gen; + if (this.reKeyTo !== undefined) { + txn.rekey = Buffer.from(this.reKeyTo.publicKey); + } + + if (!txn.caid) delete txn.caid; + if ( + !txn.apar.t && + !txn.apar.un && + !txn.apar.an && + !txn.apar.df && + !txn.apar.m && + !txn.apar.r && + !txn.apar.f && + !txn.apar.c && + !txn.apar.au && + !txn.apar.am && + !txn.apar.dc + ) { + delete txn.apar; + } else { + if (!txn.apar.t) delete txn.apar.t; + if (!txn.apar.dc) delete txn.apar.dc; + if (!txn.apar.un) delete txn.apar.un; + if (!txn.apar.an) delete txn.apar.an; + if (!txn.apar.df) delete txn.apar.df; + if (!txn.apar.m) delete txn.apar.m; + if (!txn.apar.r) delete txn.apar.r; + if (!txn.apar.f) delete txn.apar.f; + if (!txn.apar.c) delete txn.apar.c; + if (!txn.apar.au) delete txn.apar.au; + if (!txn.apar.am) delete txn.apar.am; + } + if (txn.grp === undefined) delete txn.grp; + + return txn; + } + if (this.type === 'axfer') { + // asset transfer, acceptance, revocation, mint, or burn + const txn: EncodedTransaction = { + aamt: this.amount, + fee: this.fee, + fv: this.firstRound, + lv: this.lastRound, + note: Buffer.from(this.note), + snd: Buffer.from(this.from.publicKey), + arcv: Buffer.from(this.to.publicKey), + type: this.type, + gen: this.genesisID, + gh: this.genesisHash, + lx: Buffer.from(this.lease), + grp: this.group, + xaid: this.assetIndex, + }; + if (this.closeRemainderTo !== undefined) + txn.aclose = Buffer.from(this.closeRemainderTo.publicKey); + if (this.assetRevocationTarget !== undefined) + txn.asnd = Buffer.from(this.assetRevocationTarget.publicKey); + // allowed zero values + if (!txn.note.length) delete txn.note; + if (!txn.lx.length) delete txn.lx; + if (!txn.aamt) delete txn.aamt; + if (!txn.amt) delete txn.amt; + if (!txn.fee) delete txn.fee; + if (!txn.fv) delete txn.fv; + if (!txn.gen) delete txn.gen; + if (txn.grp === undefined) delete txn.grp; + if (!txn.aclose) delete txn.aclose; + if (!txn.asnd) delete txn.asnd; + if (!txn.rekey) delete txn.rekey; + if (this.reKeyTo !== undefined) { + txn.rekey = Buffer.from(this.reKeyTo.publicKey); + } + return txn; + } + if (this.type === 'afrz') { + // asset freeze or unfreeze + const txn: EncodedTransaction = { + fee: this.fee, + fv: this.firstRound, + lv: this.lastRound, + note: Buffer.from(this.note), + snd: Buffer.from(this.from.publicKey), + type: this.type, + gen: this.genesisID, + gh: this.genesisHash, + lx: Buffer.from(this.lease), + grp: this.group, + faid: this.assetIndex, + afrz: this.freezeState, + }; + if (this.freezeAccount !== undefined) + txn.fadd = Buffer.from(this.freezeAccount.publicKey); + // allowed zero values + if (!txn.note.length) delete txn.note; + if (!txn.lx.length) delete txn.lx; + if (!txn.amt) delete txn.amt; + if (!txn.fee) delete txn.fee; + if (!txn.fv) delete txn.fv; + if (!txn.gen) delete txn.gen; + if (!txn.afrz) delete txn.afrz; + if (txn.grp === undefined) delete txn.grp; + if (this.reKeyTo !== undefined) { + txn.rekey = Buffer.from(this.reKeyTo.publicKey); + } + return txn; + } + if (this.type === 'appl') { + // application call of some kind + const txn: EncodedTransaction = { + fee: this.fee, + fv: this.firstRound, + lv: this.lastRound, + note: Buffer.from(this.note), + snd: Buffer.from(this.from.publicKey), + type: this.type, + gen: this.genesisID, + gh: this.genesisHash, + lx: Buffer.from(this.lease), + grp: this.group, + apid: this.appIndex, + apan: this.appOnComplete, + apls: { + nui: this.appLocalInts, + nbs: this.appLocalByteSlices, + }, + apgs: { + nui: this.appGlobalInts, + nbs: this.appGlobalByteSlices, + }, + apfa: this.appForeignApps, + apas: this.appForeignAssets, + apep: this.extraPages, + apbx: translateBoxReferences( + this.boxes, + this.appForeignApps, + this.appIndex + ), + }; + if (this.reKeyTo !== undefined) { + txn.rekey = Buffer.from(this.reKeyTo.publicKey); + } + if (this.appApprovalProgram !== undefined) { + txn.apap = Buffer.from(this.appApprovalProgram); + } + if (this.appClearProgram !== undefined) { + txn.apsu = Buffer.from(this.appClearProgram); + } + if (this.appArgs !== undefined) { + txn.apaa = this.appArgs.map((arg) => Buffer.from(arg)); + } + if (this.appAccounts !== undefined) { + txn.apat = this.appAccounts.map((decodedAddress) => + Buffer.from(decodedAddress.publicKey) + ); + } + // allowed zero values + if (!txn.note.length) delete txn.note; + if (!txn.lx.length) delete txn.lx; + if (!txn.amt) delete txn.amt; + if (!txn.fee) delete txn.fee; + if (!txn.fv) delete txn.fv; + if (!txn.gen) delete txn.gen; + if (!txn.apid) delete txn.apid; + if (!txn.apls.nui) delete txn.apls.nui; + if (!txn.apls.nbs) delete txn.apls.nbs; + if (!txn.apls.nui && !txn.apls.nbs) delete txn.apls; + if (!txn.apgs.nui) delete txn.apgs.nui; + if (!txn.apgs.nbs) delete txn.apgs.nbs; + if (!txn.apaa || !txn.apaa.length) delete txn.apaa; + if (!txn.apgs.nui && !txn.apgs.nbs) delete txn.apgs; + if (!txn.apap) delete txn.apap; + if (!txn.apsu) delete txn.apsu; + if (!txn.apan) delete txn.apan; + if (!txn.apfa || !txn.apfa.length) delete txn.apfa; + if (!txn.apas || !txn.apas.length) delete txn.apas; + for (const box of txn.apbx) { + if (!box.i) delete box.i; + if (!box.n || !box.n.length) delete box.n; + } + if (!txn.apbx || !txn.apbx.length) delete txn.apbx; + if (!txn.apat || !txn.apat.length) delete txn.apat; + if (!txn.apep) delete txn.apep; + if (txn.grp === undefined) delete txn.grp; + return txn; + } + if (this.type === 'stpf') { + // state proof txn + const txn: EncodedTransaction = { + fee: this.fee, + fv: this.firstRound, + lv: this.lastRound, + note: Buffer.from(this.note), + snd: Buffer.from(this.from.publicKey), + type: this.type, + gen: this.genesisID, + gh: this.genesisHash, + lx: Buffer.from(this.lease), + sptype: this.stateProofType, + spmsg: Buffer.from(this.stateProofMessage), + sp: Buffer.from(this.stateProof), + }; + // allowed zero values + if (!txn.sptype) delete txn.sptype; + if (!txn.note.length) delete txn.note; + if (!txn.lx.length) delete txn.lx; + if (!txn.amt) delete txn.amt; + if (!txn.fee) delete txn.fee; + if (!txn.fv) delete txn.fv; + if (!txn.gen) delete txn.gen; + if (!txn.apid) delete txn.apid; + if (!txn.apaa || !txn.apaa.length) delete txn.apaa; + if (!txn.apap) delete txn.apap; + if (!txn.apsu) delete txn.apsu; + if (!txn.apan) delete txn.apan; + if (!txn.apfa || !txn.apfa.length) delete txn.apfa; + if (!txn.apas || !txn.apas.length) delete txn.apas; + if (!txn.apat || !txn.apat.length) delete txn.apat; + if (!txn.apep) delete txn.apep; + if (txn.grp === undefined) delete txn.grp; + return txn; + } + + return undefined; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(txnForEnc: EncodedTransaction): Transaction { + const txn = Object.create(this.prototype) as Transaction; + txn.name = 'Transaction'; + txn.tag = Buffer.from('TX'); + + txn.genesisID = txnForEnc.gen; + txn.genesisHash = Buffer.from(txnForEnc.gh); + if (!isTransactionType(txnForEnc.type)) { + throw new Error(`Unrecognized transaction type: ${txnForEnc.type}`); + } + txn.type = txnForEnc.type; + txn.fee = txnForEnc.fee; + txn.firstRound = txnForEnc.fv; + txn.lastRound = txnForEnc.lv; + txn.note = new Uint8Array(txnForEnc.note); + txn.lease = new Uint8Array(txnForEnc.lx); + txn.from = address.decodeAddress( + address.encodeAddress(new Uint8Array(txnForEnc.snd)) + ); + if (txnForEnc.grp !== undefined) txn.group = Buffer.from(txnForEnc.grp); + if (txnForEnc.rekey !== undefined) + txn.reKeyTo = address.decodeAddress( + address.encodeAddress(new Uint8Array(txnForEnc.rekey)) + ); + + if (txnForEnc.type === 'pay') { + txn.amount = txnForEnc.amt; + txn.to = address.decodeAddress( + address.encodeAddress(new Uint8Array(txnForEnc.rcv)) + ); + if (txnForEnc.close !== undefined) + txn.closeRemainderTo = address.decodeAddress( + address.encodeAddress(txnForEnc.close) + ); + } else if (txnForEnc.type === 'keyreg') { + if (txnForEnc.votekey !== undefined) { + txn.voteKey = Buffer.from(txnForEnc.votekey); + } + if (txnForEnc.selkey !== undefined) { + txn.selectionKey = Buffer.from(txnForEnc.selkey); + } + if (txnForEnc.sprfkey !== undefined) { + txn.stateProofKey = Buffer.from(txnForEnc.sprfkey); + } + if (txnForEnc.votekd !== undefined) { + txn.voteKeyDilution = txnForEnc.votekd; + } + if (txnForEnc.votefst !== undefined) { + txn.voteFirst = txnForEnc.votefst; + } + if (txnForEnc.votelst !== undefined) { + txn.voteLast = txnForEnc.votelst; + } + if (txnForEnc.nonpart !== undefined) { + txn.nonParticipation = txnForEnc.nonpart; + } + } else if (txnForEnc.type === 'acfg') { + // asset creation, or asset reconfigure, or asset destruction + if (txnForEnc.caid !== undefined) { + txn.assetIndex = txnForEnc.caid; + } + if (txnForEnc.apar !== undefined) { + txn.assetTotal = txnForEnc.apar.t; + txn.assetDefaultFrozen = txnForEnc.apar.df; + if (txnForEnc.apar.dc !== undefined) + txn.assetDecimals = txnForEnc.apar.dc; + if (txnForEnc.apar.m !== undefined) + txn.assetManager = address.decodeAddress( + address.encodeAddress(new Uint8Array(txnForEnc.apar.m)) + ); + if (txnForEnc.apar.r !== undefined) + txn.assetReserve = address.decodeAddress( + address.encodeAddress(new Uint8Array(txnForEnc.apar.r)) + ); + if (txnForEnc.apar.f !== undefined) + txn.assetFreeze = address.decodeAddress( + address.encodeAddress(new Uint8Array(txnForEnc.apar.f)) + ); + if (txnForEnc.apar.c !== undefined) + txn.assetClawback = address.decodeAddress( + address.encodeAddress(new Uint8Array(txnForEnc.apar.c)) + ); + if (txnForEnc.apar.un !== undefined) + txn.assetUnitName = txnForEnc.apar.un; + if (txnForEnc.apar.an !== undefined) txn.assetName = txnForEnc.apar.an; + if (txnForEnc.apar.au !== undefined) txn.assetURL = txnForEnc.apar.au; + if (txnForEnc.apar.am !== undefined) + txn.assetMetadataHash = txnForEnc.apar.am; + } + } else if (txnForEnc.type === 'axfer') { + // asset transfer, acceptance, revocation, mint, or burn + if (txnForEnc.xaid !== undefined) { + txn.assetIndex = txnForEnc.xaid; + } + if (txnForEnc.aamt !== undefined) txn.amount = txnForEnc.aamt; + if (txnForEnc.aclose !== undefined) { + txn.closeRemainderTo = address.decodeAddress( + address.encodeAddress(new Uint8Array(txnForEnc.aclose)) + ); + } + if (txnForEnc.asnd !== undefined) { + txn.assetRevocationTarget = address.decodeAddress( + address.encodeAddress(new Uint8Array(txnForEnc.asnd)) + ); + } + txn.to = address.decodeAddress( + address.encodeAddress(new Uint8Array(txnForEnc.arcv)) + ); + } else if (txnForEnc.type === 'afrz') { + if (txnForEnc.afrz !== undefined) { + txn.freezeState = txnForEnc.afrz; + } + if (txnForEnc.faid !== undefined) { + txn.assetIndex = txnForEnc.faid; + } + txn.freezeAccount = address.decodeAddress( + address.encodeAddress(new Uint8Array(txnForEnc.fadd)) + ); + } else if (txnForEnc.type === 'appl') { + if (txnForEnc.apid !== undefined) { + txn.appIndex = txnForEnc.apid; + } + if (txnForEnc.apan !== undefined) { + txn.appOnComplete = txnForEnc.apan; + } + if (txnForEnc.apls !== undefined) { + if (txnForEnc.apls.nui !== undefined) + txn.appLocalInts = txnForEnc.apls.nui; + if (txnForEnc.apls.nbs !== undefined) + txn.appLocalByteSlices = txnForEnc.apls.nbs; + } + if (txnForEnc.apgs !== undefined) { + if (txnForEnc.apgs.nui !== undefined) + txn.appGlobalInts = txnForEnc.apgs.nui; + if (txnForEnc.apgs.nbs !== undefined) + txn.appGlobalByteSlices = txnForEnc.apgs.nbs; + } + if (txnForEnc.apep !== undefined) { + txn.extraPages = txnForEnc.apep; + } + if (txnForEnc.apap !== undefined) { + txn.appApprovalProgram = new Uint8Array(txnForEnc.apap); + } + if (txnForEnc.apsu !== undefined) { + txn.appClearProgram = new Uint8Array(txnForEnc.apsu); + } + if (txnForEnc.apaa !== undefined) { + txn.appArgs = txnForEnc.apaa.map((arg) => new Uint8Array(arg)); + } + if (txnForEnc.apat !== undefined) { + txn.appAccounts = txnForEnc.apat.map((addressBytes) => + address.decodeAddress( + address.encodeAddress(new Uint8Array(addressBytes)) + ) + ); + } + if (txnForEnc.apfa !== undefined) { + txn.appForeignApps = txnForEnc.apfa; + } + if (txnForEnc.apas !== undefined) { + txn.appForeignAssets = txnForEnc.apas; + } + if (txnForEnc.apbx !== undefined) { + txn.boxes = txnForEnc.apbx.map((box) => ({ + // We return 0 for the app ID so that it's guaranteed translateBoxReferences will + // translate the app index back to 0. If we instead returned the called app ID, + // translateBoxReferences would translate the app index to a nonzero value if the called + // app is also in the foreign app array. + appIndex: box.i ? txn.appForeignApps[box.i - 1] : 0, + name: box.n, + })); + } + } else if (txnForEnc.type === 'stpf') { + if (txnForEnc.sptype !== undefined) { + txn.stateProofType = txnForEnc.sptype; + } + if (txnForEnc.sp !== undefined) { + txn.stateProof = txnForEnc.sp; + } + if (txnForEnc.spmsg !== undefined) { + txn.stateProofMessage = txnForEnc.spmsg; + } + } + return txn; + } + + estimateSize() { + return this.toByte().length + NUM_ADDL_BYTES_AFTER_SIGNING; + } + + bytesToSign() { + const encodedMsg = this.toByte(); + return Buffer.from(utils.concatArrays(this.tag, encodedMsg)); + } + + toByte() { + return encoding.encode(this.get_obj_for_encoding()); + } + + // returns the raw signature + rawSignTxn(sk: Uint8Array) { + const toBeSigned = this.bytesToSign(); + const sig = nacl.sign(toBeSigned, sk); + return Buffer.from(sig); + } + + signTxn(sk: Uint8Array) { + // construct signed message + const sTxn: EncodedSignedTransaction = { + sig: this.rawSignTxn(sk), + txn: this.get_obj_for_encoding(), + }; + // add AuthAddr if signing with a different key than From indicates + const keypair = nacl.keyPairFromSecretKey(sk); + const pubKeyFromSk = keypair.publicKey; + if ( + address.encodeAddress(pubKeyFromSk) !== + address.encodeAddress(this.from.publicKey) + ) { + sTxn.sgnr = Buffer.from(pubKeyFromSk); + } + return new Uint8Array(encoding.encode(sTxn)); + } + + attachSignature(signerAddr: string, signature: Uint8Array) { + if (!nacl.isValidSignatureLength(signature.length)) { + throw new Error('Invalid signature length'); + } + const sTxn: EncodedSignedTransaction = { + sig: Buffer.from(signature), + txn: this.get_obj_for_encoding(), + }; + // add AuthAddr if signing with a different key than From indicates + if (signerAddr !== address.encodeAddress(this.from.publicKey)) { + const signerPublicKey = address.decodeAddress(signerAddr).publicKey; + sTxn.sgnr = Buffer.from(signerPublicKey); + } + return new Uint8Array(encoding.encode(sTxn)); + } + + rawTxID() { + const enMsg = this.toByte(); + const gh = Buffer.from(utils.concatArrays(this.tag, enMsg)); + return Buffer.from(nacl.genericHash(gh)); + } + + txID() { + const hash = this.rawTxID(); + return base32.encode(hash).slice(0, ALGORAND_TRANSACTION_LENGTH); + } + + // add a lease to a transaction not yet having + // supply feePerByte to increment fee accordingly + addLease(lease: Uint8Array, feePerByte = 0) { + let mutableLease: Uint8Array; + + if (lease !== undefined) { + if (lease.constructor !== Uint8Array) + throw Error('lease must be a Uint8Array.'); + if (lease.length !== ALGORAND_TRANSACTION_LEASE_LENGTH) + throw Error( + `lease must be of length ${ALGORAND_TRANSACTION_LEASE_LENGTH.toString()}.` + ); + + mutableLease = new Uint8Array(lease); + } else { + mutableLease = new Uint8Array(0); + } + this.lease = mutableLease; + if (feePerByte !== 0) { + this.fee += + (ALGORAND_TRANSACTION_LEASE_LABEL_LENGTH + + ALGORAND_TRANSACTION_LEASE_LENGTH) * + feePerByte; + } + } + + // add the rekey-to field to a transaction not yet having it + // supply feePerByte to increment fee accordingly + addRekey(reKeyTo: string, feePerByte = 0) { + if (reKeyTo !== undefined) { + this.reKeyTo = address.decodeAddress(reKeyTo); + } + if (feePerByte !== 0) { + this.fee += + (ALGORAND_TRANSACTION_REKEY_LABEL_LENGTH + + ALGORAND_TRANSACTION_ADDRESS_LENGTH) * + feePerByte; + } + } + + // build display dict for prettyPrint and toString + // eslint-disable-next-line no-underscore-dangle + _getDictForDisplay() { + const forPrinting: TransactionStorageStructure & Record = { + ...this, + }; + forPrinting.tag = forPrinting.tag.toString(); + forPrinting.from = address.encodeAddress( + (forPrinting.from as Address).publicKey + ); + if (forPrinting.to !== undefined) + forPrinting.to = address.encodeAddress( + (forPrinting.to as Address).publicKey + ); + // things that need fixing: + if (forPrinting.freezeAccount !== undefined) + forPrinting.freezeAccount = address.encodeAddress( + (forPrinting.freezeAccount as Address).publicKey + ); + if (forPrinting.closeRemainderTo !== undefined) + forPrinting.closeRemainderTo = address.encodeAddress( + (forPrinting.closeRemainderTo as Address).publicKey + ); + if (forPrinting.assetManager !== undefined) + forPrinting.assetManager = address.encodeAddress( + (forPrinting.assetManager as Address).publicKey + ); + if (forPrinting.assetReserve !== undefined) + forPrinting.assetReserve = address.encodeAddress( + (forPrinting.assetReserve as Address).publicKey + ); + if (forPrinting.assetFreeze !== undefined) + forPrinting.assetFreeze = address.encodeAddress( + (forPrinting.assetFreeze as Address).publicKey + ); + if (forPrinting.assetClawback !== undefined) + forPrinting.assetClawback = address.encodeAddress( + (forPrinting.assetClawback as Address).publicKey + ); + if (forPrinting.assetRevocationTarget !== undefined) + forPrinting.assetRevocationTarget = address.encodeAddress( + (forPrinting.assetRevocationTarget as Address).publicKey + ); + if (forPrinting.reKeyTo !== undefined) + forPrinting.reKeyTo = address.encodeAddress( + (forPrinting.reKeyTo as Address).publicKey + ); + forPrinting.genesisHash = forPrinting.genesisHash.toString('base64'); + return forPrinting; + } + + // pretty print the transaction to console + prettyPrint() { + // eslint-disable-next-line no-underscore-dangle,no-console + console.log(this._getDictForDisplay()); + } + + // get string representation + toString() { + // eslint-disable-next-line no-underscore-dangle + return JSON.stringify(this._getDictForDisplay()); + } +} + +/** + * encodeUnsignedSimulateTransaction takes a txnBuilder.Transaction object, + * converts it into a SignedTransaction-like object, and converts it to a Buffer. + * + * Note: this function should only be used to simulate unsigned transactions. + * + * @param transactionObject - Transaction object to simulate. + */ +export function encodeUnsignedSimulateTransaction( + transactionObject: Transaction +) { + const objToEncode: EncodedSignedTransaction = { + txn: transactionObject.get_obj_for_encoding(), + }; + return encoding.encode(objToEncode); +} + +/** + * encodeUnsignedTransaction takes a completed txnBuilder.Transaction object, such as from the makeFoo + * family of transactions, and converts it to a Buffer + * @param transactionObject - the completed Transaction object + */ +export function encodeUnsignedTransaction(transactionObject: Transaction) { + const objToEncode = transactionObject.get_obj_for_encoding(); + return encoding.encode(objToEncode); +} + +/** + * decodeUnsignedTransaction takes a Buffer (as if from encodeUnsignedTransaction) and converts it to a txnBuilder.Transaction object + * @param transactionBuffer - the Uint8Array containing a transaction + */ +export function decodeUnsignedTransaction( + transactionBuffer: ArrayLike +) { + const partlyDecodedObject = encoding.decode( + transactionBuffer + ) as EncodedTransaction; + return Transaction.from_obj_for_encoding(partlyDecodedObject); +} + +/** + * Object representing a transaction with a signature + */ +export interface SignedTransaction { + /** + * Transaction signature + */ + sig?: Buffer; + + /** + * The transaction that was signed + */ + txn: Transaction; + + /** + * Multisig structure + */ + msig?: EncodedMultisig; + + /** + * Logic signature + */ + lsig?: EncodedLogicSig; + + /** + * The signer, if signing with a different key than the Transaction type `from` property indicates + */ + sgnr?: Buffer; +} + +/** + * decodeSignedTransaction takes a Buffer (from transaction.signTxn) and converts it to an object + * containing the Transaction (txn), the signature (sig), and the auth-addr field if applicable (sgnr) + * @param transactionBuffer - the Uint8Array containing a transaction + * @returns containing a Transaction, the signature, and possibly an auth-addr field + */ +export function decodeSignedTransaction( + transactionBuffer: Uint8Array +): SignedTransaction { + const stxnDecoded = encoding.decode( + transactionBuffer + ) as EncodedSignedTransaction; + const stxn: SignedTransaction = { + ...stxnDecoded, + txn: Transaction.from_obj_for_encoding(stxnDecoded.txn), + }; + return stxn; +} + +/** + * Either a valid transaction object or an instance of the Transaction class + */ +export type TransactionLike = AnyTransaction | Transaction; + +export function instantiateTxnIfNeeded(transactionLike: TransactionLike) { + return transactionLike instanceof Transaction + ? transactionLike + : new Transaction(transactionLike); +} + +export default Transaction; diff --git a/src/types/account.ts b/src/types/account.ts new file mode 100644 index 0000000..d8bd706 --- /dev/null +++ b/src/types/account.ts @@ -0,0 +1,16 @@ +/** + * An Algorand account object. + * + * Contains an Algorand address and secret key. + */ +export default interface Account { + /** + * Algorand address + */ + addr: string; + + /** + * Secret key belonging to the Algorand address + */ + sk: Uint8Array; +} diff --git a/src/types/address.ts b/src/types/address.ts new file mode 100644 index 0000000..364e030 --- /dev/null +++ b/src/types/address.ts @@ -0,0 +1,7 @@ +/** + * Decoded Algorand address. Includes public key and checksum. + */ +export interface Address { + publicKey: Uint8Array; + checksum: Uint8Array; +} diff --git a/src/types/blockHeader.ts b/src/types/blockHeader.ts new file mode 100644 index 0000000..96f2af2 --- /dev/null +++ b/src/types/blockHeader.ts @@ -0,0 +1,81 @@ +/** + * Represents the metadata and state of a block. + * + * For more information, refer to: https://github.com/algorand/go-algorand/blob/master/data/bookkeeping/block.go + */ +export default interface BlockHeader { + /** + * Transaction fees + */ + fees: string; + + /** + * The number of leftover MicroAlgos after rewards distribution + */ + frac: number; + + /** + * Genesis ID to which this block belongs + */ + gen: string; + + /** + * Genesis hash to which this block belongs. + */ + gh: string; + + /** + * The hash of the previous block + */ + prev: string; + + /** + * Current protocol + */ + proto: string; + + /** + * Rewards rate + */ + rate: number; + + /** + * Round number + */ + rnd: number; + + /** + * Rewards recalculation round + */ + rwcalr: number; + + /** + * Rewards pool + */ + rwd: string; + + /** + * Sortition seed + */ + seed: string; + + /** + * Timestamp in seconds since epoch + */ + ts: number; + + /** + * Transaction root SHA512_256 + */ + txn: string; + + /** + * Transaction root SHA256 + */ + txn256: string; + + /** + * StateProofTracking map of type to tracking data + */ + spt: Map; +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..d7657e8 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,3 @@ +export * from './transactions'; +export * from './multisig'; +export * from './address'; diff --git a/src/types/intDecoding.ts b/src/types/intDecoding.ts new file mode 100644 index 0000000..e974b05 --- /dev/null +++ b/src/types/intDecoding.ts @@ -0,0 +1,29 @@ +/** + * Configure how integers in JSON response will be decoded. + */ +enum IntDecoding { + /** + * All integers will be decoded as Numbers, meaning any values greater than + * Number.MAX_SAFE_INTEGER will lose precision. + */ + DEFAULT = 'default', + + /** + * All integers will be decoded as Numbers, but if any values are greater than + * Number.MAX_SAFE_INTEGER an error will be thrown. + */ + SAFE = 'safe', + + /** + * Integers will be decoded as Numbers if they are less than or equal to + * Number.MAX_SAFE_INTEGER, otherwise they will be decoded as BigInts. + */ + MIXED = 'mixed', + + /** + * All integers will be decoded as BigInts. + */ + BIGINT = 'bigint', +} + +export default IntDecoding; diff --git a/src/types/multisig.ts b/src/types/multisig.ts new file mode 100644 index 0000000..c05e86b --- /dev/null +++ b/src/types/multisig.ts @@ -0,0 +1,22 @@ +/** + * Required options for creating a multisignature + * + * Documentation available at: https://developer.algorand.org/docs/features/transactions/signatures/#multisignatures + */ +export interface MultisigMetadata { + /** + * Multisig version + */ + version: number; + + /** + * Multisig threshold value. Authorization requires a subset of signatures, + * equal to or greater than the threshold value. + */ + threshold: number; + + /** + * A list of Algorand addresses representing possible signers for this multisig. Order is important. + */ + addrs: string[]; +} diff --git a/src/types/transactions/application.ts b/src/types/transactions/application.ts new file mode 100644 index 0000000..701240f --- /dev/null +++ b/src/types/transactions/application.ts @@ -0,0 +1,111 @@ +import { TransactionType, TransactionParams } from './base'; +import { ConstructTransaction } from './builder'; + +// ----------------------------------- +// > Application Create Transaction +// ----------------------------------- + +type SpecificParametersForCreate = Pick< + TransactionParams, + | 'appIndex' + | 'appOnComplete' + | 'appApprovalProgram' + | 'appClearProgram' + | 'appLocalInts' + | 'appLocalByteSlices' + | 'appGlobalInts' + | 'appGlobalByteSlices' + | 'appArgs' + | 'appAccounts' + | 'appForeignApps' + | 'appForeignAssets' + | 'boxes' + | 'extraPages' +>; + +interface OverwritesForCreate { + type?: TransactionType.appl; +} + +export type ApplicationCreateTransaction = ConstructTransaction< + SpecificParametersForCreate, + OverwritesForCreate +>; + +// ----------------------------------- +// > Application Update Transaction +// ----------------------------------- + +type SpecificParametersForUpdate = Pick< + TransactionParams, + | 'appIndex' + | 'appOnComplete' + | 'appApprovalProgram' + | 'appClearProgram' + | 'appArgs' + | 'appAccounts' + | 'appForeignApps' + | 'appForeignAssets' + | 'boxes' +>; + +interface OverwritesForUpdate { + type?: TransactionType.appl; +} + +export type ApplicationUpdateTransaction = ConstructTransaction< + SpecificParametersForUpdate, + OverwritesForUpdate +>; + +// ----------------------------------- +// > Application Delete Transaction +// ----------------------------------- + +type SpecificParametersForDelete = Pick< + TransactionParams, + | 'appIndex' + | 'appOnComplete' + | 'appArgs' + | 'appAccounts' + | 'appForeignApps' + | 'appForeignAssets' + | 'boxes' +>; + +interface OverwritesForDelete { + type?: TransactionType.appl; +} + +export type ApplicationDeleteTransaction = ConstructTransaction< + SpecificParametersForDelete, + OverwritesForDelete +>; + +// ----------------------------------- +// > Application Opt-In Transaction +// ----------------------------------- + +// Same structure as the application delete transaction +export type ApplicationOptInTransaction = ApplicationDeleteTransaction; + +// ----------------------------------- +// > Application Close Out Transaction +// ----------------------------------- + +// Same structure as the application delete transaction +export type ApplicationCloseOutTransaction = ApplicationDeleteTransaction; + +// -------------------------------------- +// > Application Clear State Transaction +// -------------------------------------- + +// Same structure as the application delete transaction +export type ApplicationClearStateTransaction = ApplicationDeleteTransaction; + +// -------------------------------------- +// > Application Call (NoOp) Transaction +// -------------------------------------- + +// Same structure as the application delete transaction +export type ApplicationNoOpTransaction = ApplicationDeleteTransaction; diff --git a/src/types/transactions/asset.ts b/src/types/transactions/asset.ts new file mode 100644 index 0000000..00db7c7 --- /dev/null +++ b/src/types/transactions/asset.ts @@ -0,0 +1,108 @@ +import { TransactionType, TransactionParams } from './base'; +import { ConstructTransaction } from './builder'; + +// ------------------------------ +// > Asset Create Transaction +// ------------------------------ + +type SpecificParametersForCreate = Pick< + TransactionParams, + | 'assetTotal' + | 'assetDecimals' + | 'assetDefaultFrozen' + | 'assetUnitName' + | 'assetName' + | 'assetURL' + | 'assetMetadataHash' + | 'assetManager' + | 'assetReserve' + | 'assetFreeze' + | 'assetClawback' +>; + +interface OverwritesForCreate { + type?: TransactionType.acfg; +} + +export type AssetCreateTransaction = ConstructTransaction< + SpecificParametersForCreate, + OverwritesForCreate +>; + +// ------------------------------ +// > Asset Config Transaction +// ------------------------------ + +type SpecificParametersForConfig = Pick< + TransactionParams, + | 'assetIndex' + | 'assetManager' + | 'assetReserve' + | 'assetFreeze' + | 'assetClawback' +>; + +interface OverwritesForConfig { + type?: TransactionType.acfg; +} + +export type AssetConfigurationTransaction = ConstructTransaction< + SpecificParametersForConfig, + OverwritesForConfig +>; + +// ------------------------------ +// > Asset Destroy Transaction +// ------------------------------ + +type SpecificParametersForDestroy = Pick; + +interface OverwritesForDestroy { + type?: TransactionType.acfg; +} + +export type AssetDestroyTransaction = ConstructTransaction< + SpecificParametersForDestroy, + OverwritesForDestroy +>; + +// ------------------------------ +// > Asset Freeze Transaction +// ------------------------------ + +type SpecificParametersForFreeze = Pick< + TransactionParams, + 'assetIndex' | 'freezeAccount' | 'freezeState' +>; + +interface OverwritesForFreeze { + type?: TransactionType.afrz; +} + +export type AssetFreezeTransaction = ConstructTransaction< + SpecificParametersForFreeze, + OverwritesForFreeze +>; + +// ------------------------------ +// > Asset Transfer Transaction +// ------------------------------ + +type SpecificParametersForTransfer = Pick< + TransactionParams, + | 'from' + | 'to' + | 'closeRemainderTo' + | 'assetRevocationTarget' + | 'amount' + | 'assetIndex' +>; + +interface OverwritesForTransfer { + type?: TransactionType.axfer; +} + +export type AssetTransferTransaction = ConstructTransaction< + SpecificParametersForTransfer, + OverwritesForTransfer +>; diff --git a/src/types/transactions/base.ts b/src/types/transactions/base.ts new file mode 100644 index 0000000..fb357a6 --- /dev/null +++ b/src/types/transactions/base.ts @@ -0,0 +1,435 @@ +/** + * Enum for application transaction types. + * + * The full list is available at https://developer.algorand.org/docs/reference/transactions/ + */ +export enum TransactionType { + /** + * Payment transaction + */ + pay = 'pay', + + /** + * Key registration transaction + */ + keyreg = 'keyreg', + + /** + * Asset configuration transaction + */ + acfg = 'acfg', + + /** + * Asset transfer transaction + */ + axfer = 'axfer', + + /** + * Asset freeze transaction + */ + afrz = 'afrz', + + /** + * Application transaction + */ + appl = 'appl', + /** + * State proof transaction + */ + stpf = 'stpf', +} + +export function isTransactionType(s: string): s is TransactionType { + return ( + s === TransactionType.pay || + s === TransactionType.keyreg || + s === TransactionType.acfg || + s === TransactionType.axfer || + s === TransactionType.afrz || + s === TransactionType.appl || + s === TransactionType.stpf + ); +} + +/** + * Enums for application transactions on-transaction-complete behavior + */ +export enum OnApplicationComplete { + /** + * NoOpOC indicates that an application transaction will simply call its + * ApprovalProgram + */ + NoOpOC, + + /** + * OptInOC indicates that an application transaction will allocate some + * LocalState for the application in the sender's account + */ + OptInOC, + + /** + * CloseOutOC indicates that an application transaction will deallocate + * some LocalState for the application from the user's account + */ + CloseOutOC, + + /** + * ClearStateOC is similar to CloseOutOC, but may never fail. This + * allows users to reclaim their minimum balance from an application + * they no longer wish to opt in to. + */ + ClearStateOC, + + /** + * UpdateApplicationOC indicates that an application transaction will + * update the ApprovalProgram and ClearStateProgram for the application + */ + UpdateApplicationOC, + + /** + * DeleteApplicationOC indicates that an application transaction will + * delete the AppParams for the application from the creator's balance + * record + */ + DeleteApplicationOC, +} + +/** + * A dict holding common-to-all-txns arguments + */ +export interface SuggestedParams { + /** + * Set this to true to specify fee as microalgos-per-txn + * If the final calculated fee is lower than the protocol minimum fee, the fee will be increased to match the minimum + */ + flatFee?: boolean; + + /** + * Integer fee per byte, in microAlgos. For a flat fee, set flatFee to true + */ + fee: number; + + /** + * First protocol round on which this txn is valid + */ + firstRound: number; + + /** + * Last protocol round on which this txn is valid + */ + lastRound: number; + + /** + * Specifies genesis ID of network in use + */ + genesisID: string; + + /** + * Specifies hash genesis block of network in use + */ + genesisHash: string; +} + +export type SuggestedParamsWithMinFee = SuggestedParams & { + /** + * Minimum fee (not per byte) required for the transaction to be confirmed + */ + minFee: number; +}; + +/** + * A grouping of the app ID and name of the box in an Uint8Array + */ +export interface BoxReference { + /** + * A unique application index + */ + appIndex: number; + + /** + * Name of box to reference + */ + name: Uint8Array; +} + +/** + * A full list of all available transaction parameters + * + * The full documentation is available at: + * https://developer.algorand.org/docs/reference/transactions/#common-fields-header-and-type + */ +export interface TransactionParams { + /** + * String representation of Algorand address of sender + */ + from: string; + + /** + * String representation of Algorand address of recipient + */ + to: string; + + /** + * Integer fee per byte, in microAlgos. For a flat fee, set flatFee to true + */ + fee: number; + + /** + * Integer amount to send + */ + amount: number | bigint; + + /** + * Integer first protocol round on which this txn is valid + */ + firstRound: number; + + /** + * Integer last protocol round on which this txn is valid + */ + lastRound: number; + + /** + * Arbitrary data for sender to store + */ + note?: Uint8Array; + + /** + * Specifies genesis ID of network in use + */ + genesisID: string; + + /** + * Specifies hash genesis block of network in use + */ + genesisHash: string; + + /** + * Lease a transaction. The sender cannot send another txn with that same lease until the last round of original txn has passed + */ + lease?: Uint8Array; + + /** + * Close out remaining account balance to this account + */ + closeRemainderTo?: string; + + /** + * Voting key bytes. For key deregistration, leave undefined + */ + voteKey: Uint8Array | string; + + /** + *Selection key bytes. For key deregistration, leave undefined + */ + selectionKey: Uint8Array | string; + + /** + * State proof key bytes. For key deregistration, leave undefined + */ + stateProofKey: Uint8Array | string; + + /** + * First round on which voteKey is valid + */ + voteFirst: number; + + /** + * Last round on which voteKey is valid + */ + voteLast: number; + + /** + * The dilution fo the 2-level participation key + */ + voteKeyDilution: number; + + /** + * Asset index uniquely specifying the asset + */ + assetIndex: number; + + /** + * Total supply of the asset + */ + assetTotal: number | bigint; + + /** + * Integer number of decimals for asset unit calcuation + */ + assetDecimals: number; + + /** + * Whether asset accounts should default to being frozen + */ + assetDefaultFrozen: boolean; + + /** + * String representation of Algorand address in charge of reserve, freeze, clawback, destruction, etc. + */ + assetManager?: string; + + /** + * String representation of Algorand address representing asset reserve + */ + assetReserve?: string; + + /** + * String representation of Algorand address with power to freeze/unfreeze asset holdings + */ + assetFreeze?: string; + + /** + * String representation of Algorand address with power to revoke asset holdings + */ + assetClawback?: string; + + /** + * Unit name for this asset + */ + assetUnitName?: string; + /** + * Name for this asset + */ + assetName?: string; + + /** + * URL relating to this asset + */ + assetURL?: string; + + /** + * Uint8Array or UTF-8 string representation of a hash commitment with respect to the asset. Must be exactly 32 bytes long. + */ + assetMetadataHash?: Uint8Array | string; + + /** + * String representation of Algorand address being frozen or unfrozen + */ + freezeAccount: string; + + /** + * true if freezeTarget should be frozen, false if freezeTarget should be allowed to transact + */ + freezeState: boolean; + + /** + * String representation of Algorand address – if provided, and if "from" is + * the asset's revocation manager, then deduct from "revocationTarget" rather than "from" + */ + assetRevocationTarget?: string; + + /** + * A unique application index + */ + appIndex: number; + + /** + * What application should do once the program has been run + */ + appOnComplete: OnApplicationComplete; + + /** + * Restricts number of ints in per-user local state + */ + appLocalInts: number; + + /** + * Restricts number of byte slices in per-user local state + */ + appLocalByteSlices: number; + + /** + * Restricts number of ints in global state + */ + appGlobalInts: number; + + /** + * Restricts number of byte slices in global state + */ + appGlobalByteSlices: number; + + /** + * The compiled TEAL that approves a transaction + */ + appApprovalProgram: Uint8Array; + + /** + * The compiled TEAL program that runs when clearing state + */ + appClearProgram: Uint8Array; + + /** + * Array of Uint8Array, any additional arguments to the application + */ + appArgs?: Uint8Array[]; + + /** + * Array of Address strings, any additional accounts to supply to the application + */ + appAccounts?: string[]; + + /** + * Array of int, any other apps used by the application, identified by index + */ + appForeignApps?: number[]; + + /** + * Array of int, any assets used by the application, identified by index + */ + appForeignAssets?: number[]; + + /** + * Transaction type + */ + type?: TransactionType; + + /** + * Set this to true to specify fee as microalgos-per-txn. + * + * If the final calculated fee is lower than the protocol minimum fee, the fee will be increased to match the minimum + */ + flatFee?: boolean; + + /** + * A dict holding common-to-all-txns arguments + */ + suggestedParams: SuggestedParams; + + /** + * String representation of the Algorand address that will be used to authorize all future transactions + */ + reKeyTo?: string; + + /** + * Set this value to true to mark this account as nonparticipating. + * + * All new Algorand accounts are participating by default. This means they earn rewards. + */ + nonParticipation?: boolean; + + /** + * Int representing extra pages of memory to rent during an application create transaction. + */ + extraPages?: number; + + /** + * A grouping of the app ID and name of the box in an Uint8Array + */ + boxes?: BoxReference[]; + + /* + * Uint64 identifying a particular configuration of state proofs. + */ + stateProofType?: number | bigint; + + /** + * Byte array containing the state proof. + */ + stateProof?: Uint8Array; + + /** + * Byte array containing the state proof message. + */ + stateProofMessage?: Uint8Array; +} diff --git a/src/types/transactions/builder.ts b/src/types/transactions/builder.ts new file mode 100644 index 0000000..0a40bb5 --- /dev/null +++ b/src/types/transactions/builder.ts @@ -0,0 +1,69 @@ +import { DistributiveOverwrite } from '../utils'; +import { TransactionParams, SuggestedParams } from './base'; + +/** + * Transaction base with suggested params as object + */ +type TransactionBaseWithSuggestedParams = Pick< + TransactionParams, + 'suggestedParams' | 'from' | 'type' | 'lease' | 'note' | 'reKeyTo' +>; + +/** + * Transaction base with suggested params included as parameters + */ +type TransactionBaseWithoutSuggestedParams = Pick< + TransactionParams, + | 'flatFee' + | 'fee' + | 'firstRound' + | 'lastRound' + | 'genesisHash' + | 'from' + | 'type' + | 'genesisID' + | 'lease' + | 'note' + | 'reKeyTo' +>; + +/** + * Transaction common fields. + * + * Base transaction type that is extended for all other transaction types. + * Suggested params must be included, either as named object or included in the rest + * of the parameters. + */ +export type TransactionBase = + | TransactionBaseWithoutSuggestedParams + | TransactionBaseWithSuggestedParams + | (TransactionBaseWithSuggestedParams & + TransactionBaseWithoutSuggestedParams); + +/** + * Transaction builder type that accepts 2 generics: + * - A: Additional parameters on top of the base transaction parameters + * - O: A set of overwrites for transaction parameters + */ +export type ConstructTransaction< + A = {}, + O extends Partial = {} +> = DistributiveOverwrite; + +/** + * Only accept transaction objects that include suggestedParams as an object + */ +export type MustHaveSuggestedParams = Extract< + T, + { suggestedParams: SuggestedParams } +>; + +/** + * Only accept transaction objects that include suggestedParams inline instead of being + * enclosed in its own property + */ +export type MustHaveSuggestedParamsInline< + T extends ConstructTransaction +> = Extract; + +export default ConstructTransaction; diff --git a/src/types/transactions/encoded.ts b/src/types/transactions/encoded.ts new file mode 100644 index 0000000..7864e4e --- /dev/null +++ b/src/types/transactions/encoded.ts @@ -0,0 +1,408 @@ +import { Buffer } from 'buffer'; + +/** + * Interfaces for the encoded transaction object. Every property is labelled with its associated Transaction type property + */ + +export interface EncodedAssetParams { + /** + * assetTotal + */ + t: number | bigint; + + /** + * assetDefaultFrozen + */ + df: boolean; + + /** + * assetDecimals + */ + dc: number; + + /** + * assetManager + */ + m?: Buffer; + + /** + * assetReserve + */ + r?: Buffer; + + /** + * assetFreeze + */ + f?: Buffer; + + /** + * assetClawback + */ + c?: Buffer; + + /** + * assetName + */ + an?: string; + + /** + * assetUnitName + */ + un?: string; + + /** + * assetURL + */ + au?: string; + + /** + * assetMetadataHash + */ + am?: Buffer; +} + +export interface EncodedLocalStateSchema { + /** + * appLocalInts + */ + nui: number; + + /** + * appLocalByteSlices + */ + nbs: number; +} + +export interface EncodedGlobalStateSchema { + /** + * appGlobalInts + */ + nui: number; + + /** + * appGlobalByteSlices + */ + nbs: number; +} + +export interface EncodedBoxReference { + /** + * index of the app ID in the foreign apps array + */ + i: number; + + /** + * box name + */ + n: Uint8Array; +} + +/** + * A rough structure for the encoded transaction object. Every property is labelled with its associated Transaction type property + */ +export interface EncodedTransaction { + /** + * fee + */ + fee?: number; + + /** + * firstRound + */ + fv?: number; + + /** + * lastRound + */ + lv: number; + + /** + * note + */ + note?: Buffer; + + /** + * from + */ + snd: Buffer; + + /** + * type + */ + type: string; + + /** + * genesisID + */ + gen: string; + + /** + * genesisHash + */ + gh: Buffer; + + /** + * lease + */ + lx?: Buffer; + + /** + * group + */ + grp?: Buffer; + + /** + * amount + */ + amt?: number | bigint; + + /** + * amount (but for asset transfers) + */ + aamt?: number | bigint; + + /** + * closeRemainderTo + */ + close?: Buffer; + + /** + * closeRemainderTo (but for asset transfers) + */ + aclose?: Buffer; + + /** + * reKeyTo + */ + rekey?: Buffer; + + /** + * to + */ + rcv?: Buffer; + + /** + * to (but for asset transfers) + */ + arcv?: Buffer; + + /** + * voteKey + */ + votekey?: Buffer; + + /** + * selectionKey + */ + selkey?: Buffer; + + /** + * stateProofKey + */ + sprfkey?: Buffer; + + /** + * voteFirst + */ + votefst?: number; + + /** + * voteLast + */ + votelst?: number; + + /** + * voteKeyDilution + */ + votekd?: number; + + /** + * nonParticipation + */ + nonpart?: boolean; + + /** + * assetIndex + */ + caid?: number; + + /** + * assetIndex (but for asset transfers) + */ + xaid?: number; + + /** + * assetIndex (but for asset freezing/unfreezing) + */ + faid?: number; + + /** + * freezeState + */ + afrz?: boolean; + + /** + * freezeAccount + */ + fadd?: Buffer; + + /** + * assetRevocationTarget + */ + asnd?: Buffer; + + /** + * See EncodedAssetParams type + */ + apar?: EncodedAssetParams; + + /** + * appIndex + */ + apid?: number; + + /** + * appOnComplete + */ + apan?: number; + + /** + * See EncodedLocalStateSchema type + */ + apls?: EncodedLocalStateSchema; + + /** + * See EncodedGlobalStateSchema type + */ + apgs?: EncodedGlobalStateSchema; + + /** + * appForeignApps + */ + apfa?: number[]; + + /** + * appForeignAssets + */ + apas?: number[]; + + /** + * appApprovalProgram + */ + apap?: Buffer; + + /** + * appClearProgram + */ + apsu?: Buffer; + + /** + * appArgs + */ + apaa?: Buffer[]; + + /** + * appAccounts + */ + apat?: Buffer[]; + + /** + * extraPages + */ + apep?: number; + + /** + * boxes + */ + apbx?: EncodedBoxReference[]; + + /* + * stateProofType + */ + sptype?: number | bigint; + + /** + * stateProof + */ + sp?: Buffer; + + /** + * stateProofMessage + */ + spmsg?: Buffer; +} + +export interface EncodedSubsig { + /** + * The public key + */ + pk: Uint8Array; + + /** + * The signature provided by the public key, if any + */ + s?: Uint8Array; +} + +/** + * A rough structure for the encoded multi signature transaction object. + * Every property is labelled with its associated `MultisigMetadata` type property + */ +export interface EncodedMultisig { + /** + * version + */ + v: number; + + /** + * threshold + */ + thr: number; + + /** + * Subset of signatures. A threshold of `thr` signors is required. + */ + subsig: EncodedSubsig[]; +} + +export interface EncodedLogicSig { + l: Uint8Array; + arg?: Uint8Array[]; + sig?: Uint8Array; + msig?: EncodedMultisig; +} + +export interface EncodedLogicSigAccount { + lsig: EncodedLogicSig; + sigkey?: Uint8Array; +} + +/** + * A structure for an encoded signed transaction object + */ +export interface EncodedSignedTransaction { + /** + * Transaction signature + */ + sig?: Buffer; + + /** + * The transaction that was signed + */ + txn: EncodedTransaction; + + /** + * Multisig structure + */ + msig?: EncodedMultisig; + + /** + * Logic signature + */ + lsig?: EncodedLogicSig; + + /** + * The signer, if signing with a different key than the Transaction type `from` property indicates + */ + sgnr?: Buffer; +} diff --git a/src/types/transactions/index.ts b/src/types/transactions/index.ts new file mode 100644 index 0000000..fc4d854 --- /dev/null +++ b/src/types/transactions/index.ts @@ -0,0 +1,72 @@ +import PaymentTxn from './payment'; +import KeyRegistrationTxn from './keyreg'; +import { + AssetCreateTransaction as AssetCreateTxn, + AssetConfigurationTransaction as AssetConfigTxn, + AssetDestroyTransaction as AssetDestroyTxn, + AssetFreezeTransaction as AssetFreezeTxn, + AssetTransferTransaction as AssetTransferTxn, +} from './asset'; +import { + ApplicationCreateTransaction as AppCreateTxn, + ApplicationUpdateTransaction as AppUpdateTxn, + ApplicationDeleteTransaction as AppDeleteTxn, + ApplicationOptInTransaction as AppOptInTxn, + ApplicationCloseOutTransaction as AppCloseOutTxn, + ApplicationClearStateTransaction as AppClearStateTxn, + ApplicationNoOpTransaction as AppNoOpTxn, +} from './application'; +import StateProofTxn from './stateproof'; + +// Utilities +export { + TransactionParams, + TransactionType, + SuggestedParams, + BoxReference, +} from './base'; +export { + MustHaveSuggestedParams, + MustHaveSuggestedParamsInline, +} from './builder'; +export * from './encoded'; + +// Transaction types +export { default as PaymentTxn } from './payment'; +export { default as KeyRegistrationTxn } from './keyreg'; +export { + AssetCreateTransaction as AssetCreateTxn, + AssetConfigurationTransaction as AssetConfigTxn, + AssetDestroyTransaction as AssetDestroyTxn, + AssetFreezeTransaction as AssetFreezeTxn, + AssetTransferTransaction as AssetTransferTxn, +} from './asset'; +export { + ApplicationCreateTransaction as AppCreateTxn, + ApplicationUpdateTransaction as AppUpdateTxn, + ApplicationDeleteTransaction as AppDeleteTxn, + ApplicationOptInTransaction as AppOptInTxn, + ApplicationCloseOutTransaction as AppCloseOutTxn, + ApplicationClearStateTransaction as AppClearStateTxn, + ApplicationNoOpTransaction as AppNoOpTxn, +} from './application'; +export { default as StateProofTxn } from './stateproof'; + +// All possible transaction types +type AnyTransaction = + | PaymentTxn + | KeyRegistrationTxn + | AssetCreateTxn + | AssetConfigTxn + | AssetDestroyTxn + | AssetFreezeTxn + | AssetTransferTxn + | AppCreateTxn + | AppUpdateTxn + | AppDeleteTxn + | AppOptInTxn + | AppCloseOutTxn + | AppClearStateTxn + | AppNoOpTxn + | StateProofTxn; +export default AnyTransaction; diff --git a/src/types/transactions/keyreg.ts b/src/types/transactions/keyreg.ts new file mode 100644 index 0000000..8856849 --- /dev/null +++ b/src/types/transactions/keyreg.ts @@ -0,0 +1,23 @@ +import { TransactionType, TransactionParams } from './base'; +import { ConstructTransaction } from './builder'; + +type SpecificParameters = Pick< + TransactionParams, + | 'voteKey' + | 'selectionKey' + | 'stateProofKey' + | 'voteFirst' + | 'voteLast' + | 'voteKeyDilution' + | 'nonParticipation' +>; + +interface Overwrites { + type?: TransactionType.keyreg; +} + +type KeyRegistrationTransaction = ConstructTransaction< + SpecificParameters, + Overwrites +>; +export default KeyRegistrationTransaction; diff --git a/src/types/transactions/payment.ts b/src/types/transactions/payment.ts new file mode 100644 index 0000000..5d5ea34 --- /dev/null +++ b/src/types/transactions/payment.ts @@ -0,0 +1,14 @@ +import { TransactionType, TransactionParams } from './base'; +import { ConstructTransaction } from './builder'; + +type SpecificParameters = Pick< + TransactionParams, + 'to' | 'amount' | 'closeRemainderTo' +>; + +interface Overwrites { + type?: TransactionType.pay; +} + +type PaymentTransaction = ConstructTransaction; +export default PaymentTransaction; diff --git a/src/types/transactions/stateproof.ts b/src/types/transactions/stateproof.ts new file mode 100644 index 0000000..7f2e116 --- /dev/null +++ b/src/types/transactions/stateproof.ts @@ -0,0 +1,17 @@ +import { TransactionType, TransactionParams } from './base'; +import { ConstructTransaction } from './builder'; + +type SpecificParameters = Pick< + TransactionParams, + 'stateProofType' | 'stateProof' | 'stateProofMessage' +>; + +interface Overwrites { + type?: TransactionType.stpf; +} + +type StateProofTransaction = ConstructTransaction< + SpecificParameters, + Overwrites +>; +export default StateProofTransaction; diff --git a/src/types/utils.ts b/src/types/utils.ts new file mode 100644 index 0000000..83e7e77 --- /dev/null +++ b/src/types/utils.ts @@ -0,0 +1,63 @@ +/** + * Expands types for IntelliSense so they are more human readable + * See https://stackoverflow.com/a/69288824 + */ +export type Expand = T extends (...args: infer A) => infer R + ? (...args: Expand) => Expand + : T extends infer O + ? { [K in keyof O]: O[K] } + : never; + +/** + * Same as TypeScript's Pick, but will distribute the Pick over unions + */ +export type DistributivePick = T extends unknown + ? Pick + : never; + +/** + * Overwrite a type with properties from another type + */ +export type Overwrite> = Pick< + T, + Exclude +> & + U; + +/** + * Same as Overwrite, but will distribute the Overwrite over unions + */ +export type DistributiveOverwrite> = T extends unknown + ? Overwrite + : never; + +/** + * Mark certain keys as prohibited + */ +export type NeverAllow = { + // eslint-disable-next-line no-unused-vars + [P in K]?: never; +}; + +/** + * Rename a specific property of a type to another name + * + * Usage: RenameProperty\<\{ a: string \}, 'a', 'b'\> + * -\> \{ b: string \} + */ +export type RenameProperty = { + [P in keyof T as P extends K ? R : P]: T[P]; +}; + +/** + * Rename multiple properties of one type to another name + * + * Usage: RenameProperties\<\{ a: string, b: number \}, \{ a: 'c', b: 'd' \}\> + * -\> \{ c: string, d: number \} + */ +export type RenameProperties< + T, + R extends { + [K in keyof R]: K extends keyof T ? PropertyKey : 'Error: key not in T'; + } +> = { [P in keyof T as P extends keyof R ? R[P] : P]: T[P] }; diff --git a/src/utils/utils.ts b/src/utils/utils.ts new file mode 100644 index 0000000..6abfbc0 --- /dev/null +++ b/src/utils/utils.ts @@ -0,0 +1,124 @@ +import JSONbigWithoutConfig from 'json-bigint'; +import IntDecoding from '../types/intDecoding'; + +const JSONbig = JSONbigWithoutConfig({ useNativeBigInt: true, strict: true }); + +export interface JSONOptions { + intDecoding?: IntDecoding; +} + +/** + * Parse JSON with additional options. + * @param str - The JSON string to parse. + * @param options - Options object to configure how integers in + * this request's JSON response will be decoded. Use the `intDecoding` + * property with one of the following options: + * + * * "default": All integers will be decoded as Numbers, meaning any values greater than + * Number.MAX_SAFE_INTEGER will lose precision. + * * "safe": All integers will be decoded as Numbers, but if any values are greater than + * Number.MAX_SAFE_INTEGER an error will be thrown. + * * "mixed": Integers will be decoded as Numbers if they are less than or equal to + * Number.MAX_SAFE_INTEGER, otherwise they will be decoded as BigInts. + * * "bigint": All integers will be decoded as BigInts. + * + * Defaults to "default" if not included. + */ +export function parseJSON(str: string, options?: JSONOptions) { + const intDecoding = + options && options.intDecoding ? options.intDecoding : IntDecoding.DEFAULT; + return JSONbig.parse(str, (_, value) => { + if ( + value != null && + typeof value === 'object' && + Object.getPrototypeOf(value) == null + ) { + // JSONbig.parse objects are created with Object.create(null) and thus have a null prototype + // let us remedy that + Object.setPrototypeOf(value, Object.prototype); + } + + if (typeof value === 'bigint') { + if (intDecoding === 'safe' && value > Number.MAX_SAFE_INTEGER) { + throw new Error( + `Integer exceeds maximum safe integer: ${value.toString()}. Try parsing with a different intDecoding option.` + ); + } + if ( + intDecoding === 'bigint' || + (intDecoding === 'mixed' && value > Number.MAX_SAFE_INTEGER) + ) { + return value; + } + // JSONbig.parse converts number to BigInts if they are >= 10**15. This is smaller than + // Number.MAX_SAFE_INTEGER, so we can convert some BigInts back to normal numbers. + return Number(value); + } + + if (typeof value === 'number') { + if (intDecoding === 'bigint' && Number.isInteger(value)) { + return BigInt(value); + } + } + + return value; + }); +} + +/** + * ArrayEqual takes two arrays and return true if equal, false otherwise + */ +export function arrayEqual(a: ArrayLike, b: ArrayLike) { + if (a.length !== b.length) { + return false; + } + return Array.from(a).every((val, i) => val === b[i]); +} + +/** + * ConcatArrays takes n number arrays and returns a joint Uint8Array + * @param arrs - An arbitrary number of n array-like number list arguments + * @returns [a,b] + */ +export function concatArrays(...arrs: ArrayLike[]) { + const size = arrs.reduce((sum, arr) => sum + arr.length, 0); + const c = new Uint8Array(size); + + let offset = 0; + for (let i = 0; i < arrs.length; i++) { + c.set(arrs[i], offset); + offset += arrs[i].length; + } + + return c; +} + +/** + * Remove undefined properties from an object + * @param obj - An object, preferably one with some undefined properties + * @returns A copy of the object with undefined properties removed + */ +export function removeUndefinedProperties( + obj: Record +) { + const mutableCopy = { ...obj }; + Object.keys(mutableCopy).forEach((key) => { + if (typeof mutableCopy[key] === 'undefined') delete mutableCopy[key]; + }); + return mutableCopy; +} + +/** + * Check whether the environment is Node.js (as opposed to the browser) + * @returns True if Node.js environment, false otherwise + */ +export function isNode() { + return ( + // @ts-ignore + typeof process === 'object' && + // @ts-ignore + typeof process.versions === 'object' && + // @ts-ignore + typeof process.versions.node !== 'undefined' + ); +} diff --git a/src/wait.ts b/src/wait.ts new file mode 100644 index 0000000..5ade09d --- /dev/null +++ b/src/wait.ts @@ -0,0 +1,58 @@ +import Algodv2 from './client/v2/algod/algod'; + +/** + * Wait until a transaction has been confirmed or rejected by the network, or + * until 'waitRounds' number of rounds have passed. + * @param client - An Algodv2 client + * @param txid - The ID of the transaction to wait for. + * @param waitRounds - The maximum number of rounds to wait for. + * @returns A promise that, upon success, will resolve to the output of the + * `pendingTransactionInformation` call for the confirmed transaction. + */ +export async function waitForConfirmation( + client: Algodv2, + txid: string, + waitRounds: number +): Promise> { + // Wait until the transaction is confirmed or rejected, or until 'waitRounds' + // number of rounds have passed. + + const status = await client.status().do(); + if (typeof status === 'undefined') { + throw new Error('Unable to get node status'); + } + const startRound = status['last-round'] + 1; + let currentRound = startRound; + + /* eslint-disable no-await-in-loop */ + while (currentRound < startRound + waitRounds) { + let poolError = false; + try { + const pendingInfo = await client.pendingTransactionInformation(txid).do(); + + if (pendingInfo['confirmed-round']) { + // Got the completed Transaction + return pendingInfo; + } + + if (pendingInfo['pool-error']) { + // If there was a pool error, then the transaction has been rejected + poolError = true; + throw new Error(`Transaction Rejected: ${pendingInfo['pool-error']}`); + } + } catch (err) { + // Ignore errors from PendingTransactionInformation, since it may return 404 if the algod + // instance is behind a load balancer and the request goes to a different algod than the + // one we submitted the transaction to + if (poolError) { + // Rethrow error only if it's because the transaction was rejected + throw err; + } + } + + await client.statusAfterBlock(currentRound).do(); + currentRound += 1; + } + /* eslint-enable no-await-in-loop */ + throw new Error(`Transaction not confirmed after ${waitRounds} rounds`); +} diff --git a/test-harness.sh b/test-harness.sh new file mode 100755 index 0000000..182039e --- /dev/null +++ b/test-harness.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +set -euo pipefail + +# test-harness.sh setup/start cucumber test environment. +# +# Configuration is managed with environment variables, the ones you +# are most likely to reconfigured are stored in '.test-env'. +# +# Variables: +# SDK_TESTING_URL - URL to algorand-sdk-testing, useful for forks. +# SDK_TESTING_BRANCH - branch to checkout, useful for new tests. +# SDK_TESTING_HARNESS - local directory that the algorand-sdk-testing repo is cloned into. +# VERBOSE_HARNESS - more output while the script runs. +# INSTALL_ONLY - installs feature files only, useful for unit tests. +# +# WARNING: If set to 1, new features will be LOST when downloading the test harness. +# REGARDLESS: modified features are ALWAYS overwritten. +# REMOVE_LOCAL_FEATURES - delete all local cucumber feature files before downloading these from github. +# +# WARNING: Be careful when turning on the next variable. +# In that case you'll need to provide all variables expected by `algorand-sdk-testing`'s `.env` +# OVERWRITE_TESTING_ENVIRONMENT=0 + +SHUTDOWN=0 +if [ $# -ne 0 ]; then + if [ $# -ne 1 ]; then + echo "this script accepts a single argument, which must be 'up' or 'down'." + exit 1 + fi + + case $1 in + 'up') + ;; # default. + 'down') + SHUTDOWN=1 + ;; + *) + echo "unknown parameter '$1'." + echo "this script accepts a single argument, which must be 'up' or 'down'." + exit 1 + ;; + esac +fi + +START=$(date "+%s") + +THIS=$(basename "$0") +ENV_FILE=".test-env" +TEST_DIR="tests/cucumber" + +set -a +source "$ENV_FILE" +set +a + +rootdir=$(dirname "$0") +pushd "$rootdir" + +echo "$THIS: VERBOSE_HARNESS=$VERBOSE_HARNESS" + +## Reset test harness +if [ -d "$SDK_TESTING_HARNESS" ]; then + pushd "$SDK_TESTING_HARNESS" + ./scripts/down.sh + popd + rm -rf "$SDK_TESTING_HARNESS" + if [[ $SHUTDOWN == 1 ]]; then + echo "$THIS: network shutdown complete." + exit 0 + fi +else + echo "$THIS: directory $SDK_TESTING_HARNESS does not exist - NOOP" +fi + +if [[ $SHUTDOWN == 1 ]]; then + echo "$THIS: unable to shutdown network." + exit 1 +fi + +git clone --depth 1 --single-branch --branch "$SDK_TESTING_BRANCH" "$SDK_TESTING_URL" "$SDK_TESTING_HARNESS" + + +echo "$THIS: OVERWRITE_TESTING_ENVIRONMENT=$OVERWRITE_TESTING_ENVIRONMENT" +if [[ $OVERWRITE_TESTING_ENVIRONMENT == 1 ]]; then + echo "$THIS: OVERWRITE downloaded $SDK_TESTING_HARNESS/.env with $ENV_FILE:" + cp "$ENV_FILE" "$SDK_TESTING_HARNESS"/.env +fi + +echo "$THIS: REMOVE_LOCAL_FEATURES=$REMOVE_LOCAL_FEATURES" +## Copy feature files into the project resources +if [[ $REMOVE_LOCAL_FEATURES == 1 ]]; then + echo "$THIS: OVERWRITE wipes clean $TEST_DIR/features" + if [[ $VERBOSE_HARNESS == 1 ]]; then + ( tree $TEST_DIR/features && echo "$THIS: see the previous for files deleted" ) || true + fi + rm -rf $TEST_DIR/features +fi +mkdir -p $TEST_DIR/features +cp -r "$SDK_TESTING_HARNESS"/features/* $TEST_DIR/features +if [[ $VERBOSE_HARNESS == 1 ]]; then + ( tree $TEST_DIR/features && echo "$THIS: see the previous for files copied over" ) || true +fi +echo "$THIS: seconds it took to get to end of cloning and copying: $(($(date "+%s") - START))s" + +if [[ $INSTALL_ONLY == 1 ]]; then + echo "$THIS: configured to install feature files only. Not starting test harness environment." + exit 0 +fi + +## Start test harness environment +pushd "$SDK_TESTING_HARNESS" + +[[ "$VERBOSE_HARNESS" = 1 ]] && V_FLAG="-v" || V_FLAG="" +echo "$THIS: standing up harnness with command [./up.sh $V_FLAG]" +./scripts/up.sh "$V_FLAG" + +popd +echo "$THIS: seconds it took to finish testing sdk's up.sh: $(($(date "+%s") - START))s" +echo "" +echo "--------------------------------------------------------------------------------" +echo "|" +echo "| To run sandbox commands, cd into $SDK_TESTING_HARNESS/.sandbox " +echo "|" +echo "--------------------------------------------------------------------------------" diff --git a/tests/1.Mnemonics_test.js b/tests/1.Mnemonics_test.js new file mode 100644 index 0000000..de8b183 --- /dev/null +++ b/tests/1.Mnemonics_test.js @@ -0,0 +1,68 @@ +/* eslint-env mocha */ +const assert = require('assert'); +const algosdk = require('../src/index'); +const nacl = require('../src/nacl/naclWrappers'); +const passphrase = require('../src/mnemonic/mnemonic'); + +describe('#mnemonic', () => { + it('should return a 25 words passphrase', () => { + const account = algosdk.generateAccount(); + const mn = algosdk.secretKeyToMnemonic(account.sk); + assert.strictEqual(mn.split(' ').length, 25); + }); + + it('should be able to be converted back to key', () => { + for (let i = 0; i < 50; i++) { + const seed = nacl.randomBytes(32); + const mn = passphrase.mnemonicFromSeed(seed); + const keyTarget = passphrase.seedFromMnemonic(mn); + const truncatedKey = new Uint8Array(seed); + + assert.deepStrictEqual(keyTarget, truncatedKey); + } + }); + + it('should convert pass the zero vector test', () => { + // A zero Uint8Array of length 32 + const key = new Uint8Array(Array.from({ length: 32 }, () => 0)); + const mn = + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon invest'; + const keyTarget = passphrase.mnemonicFromSeed(key); + assert.deepStrictEqual(keyTarget, mn); + }); + + it('should fail with the wrong checksum', () => { + const seed = nacl.randomBytes(32); + let mn = passphrase.mnemonicFromSeed(seed); + // Shuffle some bits + const lastChar = mn.charAt(mn.length - 1) === 'h' ? 'i' : 'h'; + mn = mn.substring(0, mn.length - 2) + lastChar; + assert.throws( + () => { + passphrase.seedFromMnemonic(mn); + }, + (err) => err.message === passphrase.FAIL_TO_DECODE_MNEMONIC_ERROR_MSG + ); + }); + + it('should fail to verify an invalid mnemonic', () => { + const mn = + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon venue abandon abandon abandon abandon abandon abandon abandon abandon abandon invest'; + assert.throws( + () => { + passphrase.seedFromMnemonic(mn); + }, + (err) => err.message === passphrase.FAIL_TO_DECODE_MNEMONIC_ERROR_MSG + ); + }); + it('should fail to verify an mnemonic with a word that is not in the list ', () => { + const mn = + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon venues abandon abandon abandon abandon abandon abandon abandon abandon abandon invest'; + assert.throws( + () => { + passphrase.seedFromMnemonic(mn); + }, + (err) => err.message === passphrase.NOT_IN_WORDS_LIST_ERROR_MSG + ); + }); +}); diff --git a/tests/10.ABI.ts b/tests/10.ABI.ts new file mode 100644 index 0000000..d8a8600 --- /dev/null +++ b/tests/10.ABI.ts @@ -0,0 +1,537 @@ +/* eslint-env mocha */ +import assert from 'assert'; +import { + ABIAddressType, + ABIArrayDynamicType, + ABIArrayStaticType, + ABIBoolType, + ABIByteType, + ABIStringType, + ABITupleType, + ABIUfixedType, + ABIUintType, + ABIType, + ABIValue, +} from '../src/abi/abi_type'; +import { + AtomicTransactionComposer, + ABIMethod, + makeBasicAccountTransactionSigner, + generateAccount, + AtomicTransactionComposerStatus, +} from '../src'; +import { decodeAddress } from '../src/encoding/address'; + +describe('ABI type checking', () => { + it('should create the correct type from the string', () => { + for (let i = 8; i < 513; i += 8) { + let expected = new ABIUintType(i); + let actual = ABIType.from(`uint${i}`); + assert.deepStrictEqual(actual, expected); + for (let j = 1; j < 161; j++) { + expected = new ABIUfixedType(i, j); + actual = ABIType.from(`ufixed${i}x${j}`); + assert.deepStrictEqual(actual, expected); + } + } + + const testCases = [ + ['address', new ABIAddressType()], + ['bool', new ABIBoolType()], + ['byte', new ABIByteType()], + ['string', new ABIStringType()], + ['uint32[]', new ABIArrayDynamicType(new ABIUintType(32))], + [ + 'byte[][]', + new ABIArrayDynamicType(new ABIArrayDynamicType(new ABIByteType())), + ], + ['ufixed256x64[]', new ABIArrayDynamicType(new ABIUfixedType(256, 64))], + [ + 'ufixed128x10[100]', + new ABIArrayStaticType(new ABIUfixedType(128, 10), 100), + ], + [ + 'bool[256][100]', + new ABIArrayStaticType( + new ABIArrayStaticType(new ABIBoolType(), 256), + 100 + ), + ], + ['()', new ABITupleType([])], + [ + '(uint16,(byte,address[10]))', + new ABITupleType([ + new ABIUintType(16), + new ABITupleType([ + new ABIByteType(), + new ABIArrayStaticType(new ABIAddressType(), 10), + ]), + ]), + ], + [ + '(uint256,(byte,address[10]),(),bool)', + new ABITupleType([ + new ABIUintType(256), + new ABITupleType([ + new ABIByteType(), + new ABIArrayStaticType(new ABIAddressType(), 10), + ]), + new ABITupleType([]), + new ABIBoolType(), + ]), + ], + [ + '(ufixed256x16,((string),bool,(address,uint8)))', + new ABITupleType([ + new ABIUfixedType(256, 16), + new ABITupleType([ + new ABITupleType([new ABIStringType()]), + new ABIBoolType(), + new ABITupleType([new ABIAddressType(), new ABIUintType(8)]), + ]), + ]), + ], + ]; + + for (const testCase of testCases) { + const actual = ABIType.from(testCase[0] as string); + assert.deepStrictEqual(actual, testCase[1]); + } + }); + it('should fail for an invalid bit size or precision', () => { + const invalidSizes = [-1, 0, 9, 513, 1024]; + const invalidPrecisions = [-1, 0, 161]; + + for (const size of invalidSizes) { + assert.throws(() => new ABIUintType(size)); + assert.throws(() => new ABIUfixedType(size, 10)); + } + for (const precision of invalidPrecisions) { + assert.throws(() => new ABIUfixedType(8, precision)); + } + }); + it('should fail for an invalid type string', () => { + const testCases = [ + // uint + 'uint 8', + 'uint8 ', + 'uint123x345', + 'uint!8', + 'uint[32]', + 'uint-893', + 'uint#120\\', + // ufixed + 'ufixed000000000016x0000010', + 'ufixed123x345', + 'ufixed 128 x 100', + 'ufixed64x10 ', + 'ufixed!8x2 ', + 'ufixed[32]x16', + 'ufixed-64x+100', + 'ufixed16x+12', + // dynamic array + 'byte[] ', + '[][][]', + 'stuff[]', + // static array + 'bool[01]', + 'byte[10 ]', + 'uint64[0x21]', + // tuple + '(ufixed128x10))', + '(,uint128,byte[])', + '(address,ufixed64x5,)', + '(byte[16],somethingwrong)', + '( )', + '((uint32)', + '(byte,,byte)', + '((byte),,(byte))', + ]; + + for (const testCase of testCases) { + assert.throws(() => ABIType.from(testCase)); + } + }); + it('should properly return whether the type is dynamic', () => { + const testCases = [ + [new ABIUintType(8).isDynamic(), false], + [new ABIUfixedType(16, 10).isDynamic(), false], + [new ABIByteType().isDynamic(), false], + [new ABIBoolType().isDynamic(), false], + [new ABIAddressType().isDynamic(), false], + [new ABIStringType().isDynamic(), true], + [new ABIArrayDynamicType(new ABIBoolType()).isDynamic(), true], + [ + new ABIArrayDynamicType( + new ABIArrayDynamicType(new ABIByteType()) + ).isDynamic(), + true, + ], + [ABIType.from('(string[100])').isDynamic(), true], + [ABIType.from('(address,bool,uint256)').isDynamic(), false], + [ABIType.from('(uint8,(byte[10]))').isDynamic(), false], + [ABIType.from('(string,uint256)').isDynamic(), true], + [ABIType.from('(bool,(ufixed16x10[],(byte,address)))').isDynamic(), true], + [ + ABIType.from('(bool,(uint256,(byte,address,string)))').isDynamic(), + true, + ], + ]; + + for (const testCase of testCases) { + const actual = testCase[0]; + const expected = testCase[1]; + assert.deepStrictEqual(actual, expected); + } + }); + it('should properly return the byte length of the type', () => { + const testCases = [ + [new ABIAddressType().byteLen(), 32], + [new ABIByteType().byteLen(), 1], + [new ABIBoolType().byteLen(), 1], + [new ABIUintType(64).byteLen(), 8], + [new ABIUfixedType(256, 50).byteLen(), 32], + [ABIType.from('bool[81]').byteLen(), 11], + [ABIType.from('bool[80]').byteLen(), 10], + [ABIType.from('bool[88]').byteLen(), 11], + [ABIType.from('address[5]').byteLen(), 160], + [ABIType.from('uint16[20]').byteLen(), 40], + [ABIType.from('ufixed64x20[10]').byteLen(), 80], + // [ABIType.from('(address,byte,ufixed16x20)').byteLen(), 35], + [ + ABIType.from( + '((bool,address[10]),(bool,bool,bool),uint8[20])' + ).byteLen(), + 342, + ], + [ABIType.from('(bool,bool)').byteLen(), 1], + [ABIType.from(`(${'bool,'.repeat(6)}uint8)`).byteLen(), 2], + [ + ABIType.from( + `(${'bool,'.repeat(10)}uint8,${'bool,'.repeat(10)}byte)` + ).byteLen(), + 6, + ], + ]; + + for (const testCase of testCases) { + const actual = testCase[0]; + const expected = testCase[1]; + assert.deepStrictEqual(actual, expected); + } + + // Dynamic types should not have a byte length + assert.throws(() => new ABIStringType().byteLen()); + assert.throws(() => new ABIArrayDynamicType(new ABIBoolType()).byteLen()); + }); +}); + +describe('ABI encoding', () => { + type TestCase = { + abiType: ABIType; + input: T; + expectedEncoding: Uint8Array; + }; + + function newTestCase(a: ABIType, b: T, c: Uint8Array): TestCase { + return { + abiType: a, + input: b, + expectedEncoding: c, + }; + } + + [ + newTestCase(new ABIUintType(8), BigInt(0), new Uint8Array([0])), + newTestCase(new ABIUintType(16), BigInt(3), new Uint8Array([0, 3])), + newTestCase( + new ABIUintType(64), + 256, + new Uint8Array([0, 0, 0, 0, 0, 0, 1, 0]) + ), + newTestCase(new ABIUfixedType(8, 30), BigInt(255), new Uint8Array([255])), + newTestCase(new ABIUfixedType(32, 10), 33, new Uint8Array([0, 0, 0, 33])), + newTestCase( + new ABIAddressType(), + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJI', + decodeAddress( + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJI' + ).publicKey + ), + newTestCase( + new ABIStringType(), + 'What’s new', + new Uint8Array([ + 0, + 12, + 87, + 104, + 97, + 116, + 226, + 128, + 153, + 115, + 32, + 110, + 101, + 119, + ]) + ), + newTestCase( + new ABIStringType(), + '😅🔨', + new Uint8Array([0, 8, 240, 159, 152, 133, 240, 159, 148, 168]) + ), + newTestCase(new ABIByteType(), 10, new Uint8Array([10])), + newTestCase(new ABIByteType(), 255, new Uint8Array([255])), + newTestCase(new ABIBoolType(), true, new Uint8Array([128])), + newTestCase(new ABIBoolType(), false, new Uint8Array([0])), + newTestCase( + new ABIStringType(), + 'asdf', + new Uint8Array([0, 4, 97, 115, 100, 102]) + ), + newTestCase( + new ABIArrayStaticType(new ABIBoolType(), 3), + [true, true, false], + new Uint8Array([192]) + ), + newTestCase( + new ABIArrayStaticType(new ABIBoolType(), 8), + [false, true, false, false, false, false, false, false], + new Uint8Array([64]) + ), + newTestCase( + new ABIArrayStaticType(new ABIBoolType(), 8), + [true, true, true, true, true, true, true, true], + new Uint8Array([255]) + ), + newTestCase( + new ABIArrayStaticType(new ABIBoolType(), 9), + [true, false, false, true, false, false, true, false, true], + new Uint8Array([146, 128]) + ), + newTestCase( + new ABIArrayStaticType(new ABIUintType(64), 3), + [BigInt(1), BigInt(2), 3], // Deliberately mix BigInt and int + new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + ]) + ), + newTestCase( + new ABIArrayDynamicType(new ABIBoolType()), + [], + new Uint8Array([0, 0]) + ), + newTestCase( + new ABIArrayDynamicType(new ABIBoolType()), + [true, true, false], + new Uint8Array([0, 3, 192]) + ), + newTestCase( + new ABIArrayDynamicType(new ABIBoolType()), + [false, true, false, false, false, false, false, false], + new Uint8Array([0, 8, 64]) + ), + newTestCase( + new ABIArrayDynamicType(new ABIBoolType()), + [true, false, false, true, false, false, true, false, true], + new Uint8Array([0, 9, 146, 128]) + ), + newTestCase(ABIType.from('()'), [], new Uint8Array([])), + // 2^6 + 2^5 = 64 + 32 = 96 + newTestCase( + ABIType.from('(bool,bool,bool)'), + [false, true, true], + new Uint8Array([96]) + ), + newTestCase( + ABIType.from('(bool[3])'), + [[false, true, true]], + new Uint8Array([96]) + ), + newTestCase( + ABIType.from('(bool[])'), + [[false, true, true]], + new Uint8Array([0, 2, 0, 3, 96]) + ), + newTestCase( + ABIType.from('(bool[2],bool[])'), + [ + [true, true], + [true, true], + ], + new Uint8Array([192, 0, 3, 0, 2, 192]) + ), + newTestCase( + ABIType.from('(bool[],bool[])'), + [[], []], + new Uint8Array([0, 4, 0, 6, 0, 0, 0, 0]) + ), + newTestCase( + ABIType.from('(string,bool,bool,bool,bool,string)'), + ['AB', true, false, true, false, 'DE'], + new Uint8Array([0, 5, 160, 0, 9, 0, 2, 65, 66, 0, 2, 68, 69]) + ), + newTestCase( + new ABITupleType([new ABIUintType(8), new ABIUintType(16)]), + [1, 2], + new Uint8Array([1, 0, 2]) + ), + ].forEach((testCase) => { + it(`should round-trip ${testCase.abiType}, ${testCase.input}`, () => { + const encoded = testCase.abiType.encode(testCase.input); + assert.deepStrictEqual(encoded, testCase.expectedEncoding); + const decoded = testCase.abiType.decode(encoded); + + // Converts any numeric type to BigInt for strict equality comparisons. + // The conversion is required because there's no type information + // available to convert a decoded BigInt back to its original number + // form. Converting from number to BigInt is always _safe_. + function numericAsBigInt(d: ABIValue): ABIValue { + if (typeof d === 'number') { + return BigInt(d); + } + if (d instanceof Array) { + return (d as ABIValue[]).map(numericAsBigInt); + } + return d; + } + + // Returns true when the provided ABIType decodes to BigInt. + function decodeReturnsBigInt(t: ABIType): boolean { + if (t instanceof ABIUintType || t instanceof ABIUfixedType) { + return true; + } + if (t instanceof ABITupleType) { + return t.childTypes.map(decodeReturnsBigInt).includes(true); + } + if (t instanceof ABIArrayStaticType) { + return decodeReturnsBigInt(t.childType); + } + if (t instanceof ABIArrayDynamicType) { + return decodeReturnsBigInt(t.childType); + } + return false; + } + + if (decodeReturnsBigInt(testCase.abiType)) { + // If casting to BigInt changes the test case input, then it implies + // the _unchanged_ test case input != `decoded`. + // + // The sanity check confirms that transforming the expected value is + // necessary. + if (testCase.input !== numericAsBigInt(testCase.input)) { + assert.notDeepStrictEqual(decoded, testCase.input); + } + + assert.deepStrictEqual(decoded, numericAsBigInt(testCase.input)); + } else { + assert.deepStrictEqual(decoded, testCase.input); + } + }); + }); + + it('should fail for bad values during encoding', () => { + assert.throws(() => new ABIUintType(8).encode(BigInt(-1))); + assert.throws(() => new ABIUintType(512).encode(BigInt(2 ** 512))); + assert.throws(() => new ABIUfixedType(512, 10).encode(BigInt(-1))); + assert.throws(() => new ABIByteType().encode(-1)); + assert.throws(() => new ABIByteType().encode(256)); + assert.throws(() => new ABIAddressType().encode('BADADDRESS')); + assert.throws(() => + new ABIArrayStaticType(new ABIBoolType(), 3).encode([true]) + ); + assert.throws(() => + new ABIArrayStaticType(new ABIStringType(), 1).encode([true]) + ); + assert.throws(() => + new ABIArrayStaticType(new ABIUintType(256), 1).encode(['hello']) + ); + assert.throws(() => + new ABIArrayDynamicType(new ABIAddressType()).encode([false]) + ); + assert.throws(() => + new ABITupleType([new ABIBoolType(), new ABIUfixedType(128, 20)]).encode([ + BigInt(3), + true, + ]) + ); + }); + + it('should properly accept foreign array objects in the ATC addMethodCall', () => { + const composer = new AtomicTransactionComposer(); + const method = ABIMethod.fromSignature('add(application)uint8'); + const account = generateAccount(); + const sender = 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA'; + const sp = { + fee: 1000, + firstRound: 1, + lastRound: 1001, + genesisID: 'gi', + genesisHash: 'gh', + }; + const foreignAcct = + 'E4VCHISDQPLIZWMALIGNPK2B2TERPDMR64MZJXE3UL75MUDXZMADX5OWXM'; + + // Create method call using ATC. + // The foreign apps array argument should be packed before the method argument. + composer.addMethodCall({ + appID: 7, + method, + sender, + suggestedParams: sp, + methodArgs: [2], + appAccounts: [foreignAcct], + appForeignApps: [1], + appForeignAssets: [124], + signer: makeBasicAccountTransactionSigner(account), + }); + + assert.deepStrictEqual( + composer.getStatus(), + AtomicTransactionComposerStatus.BUILDING + ); + assert.deepStrictEqual(composer.count(), 1); + + // The built group should have one txn. + const txns = composer.buildGroup(); + // eslint-disable-next-line prefer-destructuring + const txn = txns[0].txn; + + // Assert that foreign objects were passed in and ordering was correct. + assert.deepStrictEqual(txn.appForeignApps?.length, 2); + assert.deepStrictEqual(txn.appForeignApps[0], 1); + assert.deepStrictEqual(txn.appForeignApps[1], 2); + + assert.deepStrictEqual(txn.appForeignAssets?.length, 1); + assert.deepStrictEqual(txn.appForeignAssets[0], 124); + + assert.deepStrictEqual(txn.appAccounts?.length, 1); + assert.deepStrictEqual(txn.appAccounts[0], decodeAddress(foreignAcct)); + }); +}); diff --git a/tests/2.Encoding.js b/tests/2.Encoding.js new file mode 100644 index 0000000..b3f6ca0 --- /dev/null +++ b/tests/2.Encoding.js @@ -0,0 +1,541 @@ +/* eslint-env mocha */ +const { Buffer } = require('buffer'); +const assert = require('assert'); +const algosdk = require('../src/index'); +const utils = require('../src/utils/utils'); + +const ERROR_CONTAINS_EMPTY_STRING = + 'The object contains empty or 0 values. First empty or 0 value encountered during encoding: '; + +describe('encoding', () => { + it('should be able to encode and decode', () => { + const temp = { a: 3, b: 500 }; + const enc = algosdk.encodeObj(temp); + const dec = algosdk.decodeObj(enc); + assert.deepStrictEqual(temp, dec); + }); + // The strategy here is mainly to see that we match our go code. + // This will check consistency with golden that were produced by protocol.encoding + describe('#encode', () => { + it('should match encode every integer must be encoded to the smallest type possible', () => { + let golden = Buffer.from([0x81, 0xa1, 0x41, 0x78]); + let o = { A: 120 }; + assert.notStrictEqual(algosdk.encodeObj(o), golden); + + golden = Buffer.from([0x81, 0xa1, 0x41, 0xcd, 0x1, 0x2c]); + o = { A: 300 }; + assert.notStrictEqual(algosdk.encodeObj(o), golden); + }); + + it('should sort all fields before encoding', () => { + const a = { a: 3, b: 5 }; + const b = { b: 5, a: 3 }; + assert.notStrictEqual(algosdk.encodeObj(a), algosdk.encodeObj(b)); + }); + + it('should fail if empty or 0 fields exist', () => { + const a = { a: 0, B: [] }; + assert.throws( + () => { + algosdk.encodeObj(a); + }, + (err) => err.toString().includes(ERROR_CONTAINS_EMPTY_STRING) + ); + + const b = { a: 4, B: [] }; + assert.throws( + () => { + algosdk.encodeObj(b); + }, + (err) => err.toString().includes(ERROR_CONTAINS_EMPTY_STRING) + ); + + const c = { a: 4, B: 0 }; + assert.throws( + () => { + algosdk.encodeObj(c); + }, + (err) => err.toString().includes(ERROR_CONTAINS_EMPTY_STRING) + ); + }); + + it('should encode Binary blob should be used for binary data and string for strings', () => { + // prettier-ignore + const golden = Buffer.from([0x82, 0xa1, 0x4a, 0xc4, 0x3, 0x14, 0x1e, 0x28, 0xa1, 0x4b, 0xa3, 0x61, 0x61, 0x61]); + const o = { J: Buffer.from([20, 30, 40]), K: 'aaa' }; + assert.notStrictEqual(algosdk.encodeObj(o), golden); + }); + + it('should safely encode/decode bigints', () => { + const beforeZero = BigInt('0'); + const afterZero = algosdk.decodeObj(algosdk.encodeObj(beforeZero)); + // eslint-disable-next-line eqeqeq + assert.ok(beforeZero == afterZero); // after is a Number because 0 fits into a Number - so we do this loose comparison + const beforeLarge = BigInt('18446744073709551612'); // larger than a Number, but fits into a uint64 + const afterLarge = algosdk.decodeObj(algosdk.encodeObj(beforeLarge)); + assert.strictEqual(beforeLarge, afterLarge); + const beforeTooLarge = BigInt('18446744073709551616'); // larger than even fits into a uint64. we do not want to work with these too-large numbers + const afterTooLarge = algosdk.decodeObj( + algosdk.encodeObj(beforeTooLarge) + ); + assert.notStrictEqual(beforeTooLarge, afterTooLarge); + }); + + it('should match our go code', () => { + // prettier-ignore + const golden = new Uint8Array([134, 163, 97, 109, 116, 205, 3, 79, 163, 102, 101, 101, 10, 162, 102, 118, 51, 162, 108, 118, 61, 163, 114, 99, 118, 196, 32, 145, 154, 160, 178, 192, 112, 147, 3, 73, 200, 52, 23, 24, 49, 180, 79, 91, 78, 35, 190, 125, 207, 231, 37, 41, 131, 96, 252, 244, 221, 54, 208, 163, 115, 110, 100, 196, 32, 145, 154, 160, 178, 192, 112, 147, 3, 73, 200, 52, 23, 24, 49, 180, 79, 91, 78, 35, 190, 125, 207, 231, 37, 41, 131, 96, 252, 244, 221, 54, 208]); + const ad = 'SGNKBMWAOCJQGSOIGQLRQMNUJ5NU4I56PXH6OJJJQNQPZ5G5G3IOVLI5VM'; + const o = { + snd: Buffer.from(algosdk.decodeAddress(ad).publicKey), + rcv: Buffer.from(algosdk.decodeAddress(ad).publicKey), + fee: 10, + amt: 847, + fv: 51, + lv: 61, + }; + + const jsEnc = algosdk.encodeObj(o); + assert.deepStrictEqual(jsEnc, golden); + }); + }); + + describe('uint64', () => { + it('should encode properly', () => { + const testcases = [ + [0, Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0])], + [0n, Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0])], + [1, Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1])], + [1n, Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1])], + [255, Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 255])], + [255n, Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 255])], + [256, Uint8Array.from([0, 0, 0, 0, 0, 0, 1, 0])], + [256n, Uint8Array.from([0, 0, 0, 0, 0, 0, 1, 0])], + [ + Number.MAX_SAFE_INTEGER, + Uint8Array.from([0, 31, 255, 255, 255, 255, 255, 255]), + ], + [ + BigInt(Number.MAX_SAFE_INTEGER), + Uint8Array.from([0, 31, 255, 255, 255, 255, 255, 255]), + ], + [ + BigInt(Number.MAX_SAFE_INTEGER) + 1n, + Uint8Array.from([0, 32, 0, 0, 0, 0, 0, 0]), + ], + [ + 0xffffffffffffffffn, + Uint8Array.from([255, 255, 255, 255, 255, 255, 255, 255]), + ], + ]; + + for (const [input, expected] of testcases) { + const actual = algosdk.encodeUint64(input); + assert.deepStrictEqual( + actual, + expected, + `Incorrect encoding of ${typeof input} ${input}` + ); + } + }); + + it('should not encode negative numbers', () => { + assert.throws(() => algosdk.encodeUint64(-1)); + assert.throws(() => algosdk.encodeUint64(-1n)); + assert.throws(() => algosdk.encodeUint64(Number.MIN_SAFE_INTEGER)); + assert.throws(() => + algosdk.encodeUint64(BigInt(Number.MIN_SAFE_INTEGER)) + ); + }); + + it('should not encode numbers larger than 2^64', () => { + assert.throws(() => algosdk.encodeUint64(0xffffffffffffffffn + 1n)); + }); + + it('should not encode decimals', () => { + assert.throws(() => algosdk.encodeUint64(0.01)); + assert.throws(() => algosdk.encodeUint64(9999.99)); + }); + + it('should decode properly in default mode', () => { + // should be the same as safe mode + const testcases = [ + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]), 0], + [Uint8Array.from([0]), 0], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1]), 1], + [Uint8Array.from([0, 0, 1]), 1], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 255]), 255], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 1, 0]), 256], + [ + Uint8Array.from([31, 255, 255, 255, 255, 255, 255]), + Number.MAX_SAFE_INTEGER, + ], + [ + Uint8Array.from([0, 31, 255, 255, 255, 255, 255, 255]), + Number.MAX_SAFE_INTEGER, + ], + ]; + + for (const [input, expected] of testcases) { + const actual = algosdk.decodeUint64(input); + assert.deepStrictEqual( + actual, + expected, + `Incorrect decoding of ${Array.from(input)}` + ); + } + }); + + it('should throw an error when decoding large values in default mode', () => { + assert.throws(() => + algosdk.decodeUint64(Uint8Array.from([0, 32, 0, 0, 0, 0, 0, 0])) + ); + assert.throws(() => + algosdk.decodeUint64(Uint8Array.from([0, 32, 0, 0, 0, 0, 0, 1])) + ); + assert.throws(() => + algosdk.decodeUint64( + Uint8Array.from([255, 255, 255, 255, 255, 255, 255, 255]) + ) + ); + }); + + it('should decode properly in safe mode', () => { + const testcases = [ + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]), 0], + [Uint8Array.from([0]), 0], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1]), 1], + [Uint8Array.from([0, 0, 1]), 1], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 255]), 255], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 1, 0]), 256], + [ + Uint8Array.from([31, 255, 255, 255, 255, 255, 255]), + Number.MAX_SAFE_INTEGER, + ], + [ + Uint8Array.from([0, 31, 255, 255, 255, 255, 255, 255]), + Number.MAX_SAFE_INTEGER, + ], + ]; + + for (const [input, expected] of testcases) { + const actual = algosdk.decodeUint64(input, 'safe'); + assert.deepStrictEqual( + actual, + expected, + `Incorrect decoding of ${Array.from(input)}` + ); + } + }); + + it('should throw an error when decoding large values in safe mode', () => { + assert.throws(() => + algosdk.decodeUint64(Uint8Array.from([0, 32, 0, 0, 0, 0, 0, 0]), 'safe') + ); + assert.throws(() => + algosdk.decodeUint64(Uint8Array.from([0, 32, 0, 0, 0, 0, 0, 1]), 'safe') + ); + assert.throws(() => + algosdk.decodeUint64( + Uint8Array.from([255, 255, 255, 255, 255, 255, 255, 255]), + 'safe' + ) + ); + }); + + it('should decode properly in mixed mode', () => { + const testcases = [ + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]), 0], + [Uint8Array.from([0]), 0], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1]), 1], + [Uint8Array.from([0, 0, 1]), 1], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 255]), 255], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 1, 0]), 256], + [ + Uint8Array.from([31, 255, 255, 255, 255, 255, 255]), + Number.MAX_SAFE_INTEGER, + ], + [ + Uint8Array.from([0, 31, 255, 255, 255, 255, 255, 255]), + Number.MAX_SAFE_INTEGER, + ], + [ + Uint8Array.from([0, 32, 0, 0, 0, 0, 0, 0]), + BigInt(Number.MAX_SAFE_INTEGER) + 1n, + ], + [ + Uint8Array.from([32, 0, 0, 0, 0, 0, 0]), + BigInt(Number.MAX_SAFE_INTEGER) + 1n, + ], + [ + Uint8Array.from([255, 255, 255, 255, 255, 255, 255, 255]), + 0xffffffffffffffffn, + ], + ]; + + for (const [input, expected] of testcases) { + const actual = algosdk.decodeUint64(input, 'mixed'); + assert.deepStrictEqual( + actual, + expected, + `Incorrect decoding of ${Array.from(input)}` + ); + } + }); + + it('should decode properly in bigint mode', () => { + const testcases = [ + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]), 0n], + [Uint8Array.from([0]), 0n], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1]), 1n], + [Uint8Array.from([0, 0, 1]), 1n], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 255]), 255n], + [Uint8Array.from([0, 0, 0, 0, 0, 0, 1, 0]), 256n], + [ + Uint8Array.from([31, 255, 255, 255, 255, 255, 255]), + BigInt(Number.MAX_SAFE_INTEGER), + ], + [ + Uint8Array.from([0, 31, 255, 255, 255, 255, 255, 255]), + BigInt(Number.MAX_SAFE_INTEGER), + ], + [ + Uint8Array.from([0, 32, 0, 0, 0, 0, 0, 0]), + BigInt(Number.MAX_SAFE_INTEGER) + 1n, + ], + [ + Uint8Array.from([32, 0, 0, 0, 0, 0, 0]), + BigInt(Number.MAX_SAFE_INTEGER) + 1n, + ], + [ + Uint8Array.from([255, 255, 255, 255, 255, 255, 255, 255]), + 0xffffffffffffffffn, + ], + ]; + + for (const [input, expected] of testcases) { + const actual = algosdk.decodeUint64(input, 'bigint'); + assert.deepStrictEqual( + actual, + expected, + `Incorrect decoding of ${Array.from(input)}` + ); + } + }); + + it('should throw an error when decoding data with wrong length', () => { + assert.throws(() => algosdk.decodeUint64(Uint8Array.from([]))); + assert.throws(() => + algosdk.decodeUint64(Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0, 0])) + ); + }); + + it('should throw an error when decoding with an unknown mode', () => { + assert.throws(() => + algosdk.decodeUint64( + Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]), + 'unknown' + ) + ); + }); + }); + + describe('JSON parse BigInt', () => { + it('should parse null', () => { + const input = 'null'; + + for (const intDecoding of ['default', 'safe', 'mixed', 'bigint']) { + const actual = utils.parseJSON(input, { intDecoding }); + const expected = null; + + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + } + }); + + it('should parse number', () => { + const inputs = ['17', '9007199254740991']; + for (const input of inputs) { + for (const intDecoding of ['default', 'safe', 'mixed', 'bigint']) { + const actual = utils.parseJSON(input, { intDecoding }); + const expected = + intDecoding === 'bigint' ? BigInt(input) : Number(input); + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + } + } + }); + + it('should parse empty object', () => { + const input = '{}'; + + for (const intDecoding of ['default', 'safe', 'mixed', 'bigint']) { + const actual = utils.parseJSON(input, { intDecoding }); + const expected = {}; + + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + } + }); + + it('should parse populated object', () => { + const input = '{"a":1,"b":"value","c":[1,2,3],"d":null,"e":{},"f":true}'; + + for (const intDecoding of ['default', 'safe', 'mixed', 'bigint']) { + const actual = utils.parseJSON(input, { intDecoding }); + + let expected; + if (intDecoding === 'bigint') { + expected = { + a: 1n, + b: 'value', + c: [1n, 2n, 3n], + d: null, + e: {}, + f: true, + }; + } else { + expected = { + a: 1, + b: 'value', + c: [1, 2, 3], + d: null, + e: {}, + f: true, + }; + } + + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + } + }); + + it('should parse object with BigInt', () => { + const input = + '{"a":0,"b":9007199254740991,"c":9007199254740992,"d":9223372036854775807}'; + + assert.throws(() => utils.parseJSON(input, { intDecoding: 'safe' })); + + for (const intDecoding of ['default', 'mixed', 'bigint']) { + const actual = utils.parseJSON(input, { intDecoding }); + + let expected; + if (intDecoding === 'bigint') { + expected = { + a: 0n, + b: 9007199254740991n, + c: 9007199254740992n, + d: 9223372036854775807n, + }; + } else if (intDecoding === 'mixed') { + expected = { + a: 0, + b: 9007199254740991, + c: 9007199254740992n, + d: 9223372036854775807n, + }; + } else { + expected = { + a: 0, + b: 9007199254740991, + c: Number(9007199254740992n), + d: Number(9223372036854775807n), + }; + } + + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + } + }); + + it('should parse empty array', () => { + const input = '[]'; + + for (const intDecoding of ['default', 'safe', 'mixed', 'bigint']) { + const actual = utils.parseJSON(input, { intDecoding }); + const expected = []; + + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + } + }); + + it('should parse populated array', () => { + const input = '["test",2,null,[7],{"a":9.5},true]'; + + for (const intDecoding of ['default', 'safe', 'mixed', 'bigint']) { + const actual = utils.parseJSON(input, { intDecoding }); + + let expected; + if (intDecoding === 'bigint') { + expected = ['test', 2n, null, [7n], { a: 9.5 }, true]; + } else { + expected = ['test', 2, null, [7], { a: 9.5 }, true]; + } + + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + } + }); + + it('should parse array with BigInt', () => { + const input = '[0,9007199254740991,9007199254740992,9223372036854775807]'; + + assert.throws(() => utils.parseJSON(input, { intDecoding: 'safe' })); + + for (const intDecoding of ['default', 'mixed', 'bigint']) { + const actual = utils.parseJSON(input, { intDecoding }); + + let expected; + if (intDecoding === 'bigint') { + expected = [ + 0n, + 9007199254740991n, + 9007199254740992n, + 9223372036854775807n, + ]; + } else if (intDecoding === 'mixed') { + expected = [ + 0, + 9007199254740991, + 9007199254740992n, + 9223372036854775807n, + ]; + } else { + expected = [ + 0, + 9007199254740991, + Number(9007199254740992n), + Number(9223372036854775807n), + ]; + } + + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + } + }); + }); +}); diff --git a/tests/3.Address.js b/tests/3.Address.js new file mode 100644 index 0000000..73ac2b2 --- /dev/null +++ b/tests/3.Address.js @@ -0,0 +1,115 @@ +/* eslint-env mocha */ +const assert = require('assert'); +const nacl = require('../src/nacl/naclWrappers'); +const algosdk = require('../src/index'); +const address = require('../src/encoding/address'); + +describe('address', () => { + describe('#isValid', () => { + const correctCase = + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJI'; + // prettier-ignore + const correctPublicKey = new Uint8Array([99, 180, 127, 102, 156, 252, 55, 227, 39, 198, 169, 232, 163, 16, 36, 130, 26, 223, 230, 213, 0, 153, 108, 192, 200, 197, 28, 77, 196, 50, 141, 112]); + const correctChecksum = new Uint8Array([122, 240, 2, 74]); + const malformedAddress1 = + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJ'; + const maldformedAddress2 = 123; + const malformedAddress3 = + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACererZQGI113RBSRVYHV4ACJI'; + const wrongChecksumAddress = + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJG'; + + // Check core functions + it('should verify a valid Algorand address', () => { + const decodedAddress = algosdk.decodeAddress(correctCase); + assert.deepStrictEqual(decodedAddress.publicKey, correctPublicKey); + assert.deepStrictEqual(decodedAddress.checksum, correctChecksum); + }); + + it('should fail to verify a malformed Algorand address', () => { + assert.throws( + () => { + algosdk.decodeAddress(malformedAddress1); + }, + (err) => err.message === address.MALFORMED_ADDRESS_ERROR_MSG + ); + assert.throws( + () => { + algosdk.decodeAddress(maldformedAddress2); + }, + (err) => err.message === address.MALFORMED_ADDRESS_ERROR_MSG + ); + // Catch an exception possibly thrown by base32 decoding function + assert.throws( + () => { + algosdk.decodeAddress(malformedAddress3); + }, + (err) => err.message === 'Invalid base32 characters' + ); + }); + + it('should fail to verify a checksum for an invalid Algorand address', () => { + assert.throws( + () => { + algosdk.decodeAddress(wrongChecksumAddress); + }, + (err) => err.message === address.CHECKSUM_ADDRESS_ERROR_MSG + ); + }); + + // Check helper functions + it('should verify a valid Algorand address', () => { + assert.ok(algosdk.isValidAddress(correctCase)); + }); + + it('should fail to verify an invalid Algorand address', () => { + assert.strictEqual(algosdk.isValidAddress(malformedAddress1), false); + }); + }); + + describe('encode, decode', () => { + it('should be able to encode and verify an address', () => { + const pk = nacl.randomBytes(32); + const addr = algosdk.encodeAddress(pk); + assert.ok(algosdk.isValidAddress(addr)); + }); + + it('should be able to encode and decode an address', () => { + const pk = nacl.randomBytes(32); + const addr = algosdk.encodeAddress(pk); + const d = algosdk.decodeAddress(addr); + assert.deepStrictEqual(new Uint8Array(d.publicKey), pk); + }); + }); + + describe('from multisig preimage', () => { + it('should match main repo code', () => { + const addr1 = + 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const addr2 = + 'HTNOX33OCQI2JCOLZ2IRM3BC2WZ6JUILSLEORBPFI6W7GU5Q4ZW6LINHLA'; + const addr3 = + 'E6JSNTY4PVCY3IRZ6XEDHEO6VIHCQ5KGXCIQKFQCMB2N6HXRY4IB43VSHI'; + const params = { + version: 1, + threshold: 2, + addrs: [addr1, addr2, addr3], + }; + const expectAddr = + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM'; + const actualAddr = algosdk.multisigAddress(params); + assert.ok(algosdk.isValidAddress(actualAddr)); + assert.deepStrictEqual(actualAddr, expectAddr); + }); + }); + + describe('#getApplicationAddress', () => { + it('should produce the correct address', () => { + const appID = 77; + const expected = + 'PCYUFPA2ZTOYWTP43MX2MOX2OWAIAXUDNC2WFCXAGMRUZ3DYD6BWFDL5YM'; + const actual = algosdk.getApplicationAddress(appID); + assert.strictEqual(actual, expected); + }); + }); +}); diff --git a/tests/4.Utils.ts b/tests/4.Utils.ts new file mode 100644 index 0000000..a2b589d --- /dev/null +++ b/tests/4.Utils.ts @@ -0,0 +1,44 @@ +/* eslint-env mocha */ +import assert from 'assert'; +import * as utils from '../src/utils/utils'; +import * as nacl from '../src/nacl/naclWrappers'; + +describe('utils', () => { + describe('concatArrays', () => { + it('should concat two Uint8Arrays', () => { + const a = new Uint8Array([1, 2, 3]); + const b = new Uint8Array([4, 5, 6]); + + const expected = new Uint8Array([1, 2, 3, 4, 5, 6]); + const actual = utils.concatArrays(a, b); + assert.deepStrictEqual(actual, expected); + }); + + it('should concat two number arrays as a Uint8Array', () => { + const a = [1, 2, 3]; + const b = [4, 5, 6]; + + const expected = new Uint8Array([1, 2, 3, 4, 5, 6]); + const actual = utils.concatArrays(a, b); + assert.deepStrictEqual(actual, expected); + assert(actual instanceof Uint8Array); + }); + + it('should concat three Uint8Arrays', () => { + const a = new Uint8Array([1, 2]); + const b = new Uint8Array([3, 4]); + const c = new Uint8Array([5, 6]); + + const expected = new Uint8Array([1, 2, 3, 4, 5, 6]); + const actual = utils.concatArrays(a, b, c); + assert.deepStrictEqual(expected, actual); + }); + }); +}); + +describe('nacl wrapper', () => { + it('should validate signature length', () => { + assert.strictEqual(nacl.isValidSignatureLength(6), false); + assert.strictEqual(nacl.isValidSignatureLength(64), true); + }); +}); diff --git a/tests/5.Transaction.js b/tests/5.Transaction.js new file mode 100644 index 0000000..7069d4b --- /dev/null +++ b/tests/5.Transaction.js @@ -0,0 +1,1648 @@ +/* eslint-env mocha */ +const { Buffer } = require('buffer'); +const assert = require('assert'); +const algosdk = require('../src/index'); +const { translateBoxReferences } = require('../src/boxStorage'); +const group = require('../src/group'); + +describe('Sign', () => { + /* eslint-disable no-console */ + const originalLogFunction = console.log; + let logs; + + beforeEach(() => { + logs = ''; + + // Mock console.log to suppress logs during tests + console.log = (msg) => { + logs += `${msg}\n`; + }; + }); + + afterEach(function Cleanup() { + // Unmock console.log + console.log = originalLogFunction; + + // Unsuppress logs if the test failed + if (this.currentTest.state === 'failed') { + console.log(logs); + } + }); + /* eslint-enable no-console */ + + it('should not modify input arrays', () => { + const appArgs = [Uint8Array.from([1, 2]), Uint8Array.from([3, 4])]; + const appAccounts = [ + '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + ]; + const appForeignApps = [17, 200]; + const appForeignAssets = [7, 8, 9]; + const boxes = [{ appIndex: 0, name: Uint8Array.from([0]) }]; + const o = { + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 10, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: new Uint8Array(0), + type: 'appl', + appIndex: 5, + appArgs, + appAccounts, + appForeignApps, + appForeignAssets, + boxes, + }; + const txn = new algosdk.Transaction(o); + assert.deepStrictEqual(appArgs, [ + Uint8Array.from([1, 2]), + Uint8Array.from([3, 4]), + ]); + assert.deepStrictEqual(appAccounts, [ + '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + ]); + assert.deepStrictEqual(appForeignApps, [17, 200]); + assert.deepStrictEqual(appForeignAssets, [7, 8, 9]); + assert.ok(txn.appArgs !== appArgs); + assert.ok(txn.appAccounts !== appAccounts); + assert.ok(txn.appForeignApps !== appForeignApps); + assert.ok(txn.appForeignAssets !== appForeignAssets); + assert.ok(txn.boxes !== boxes); + }); + + it('should not complain on a missing note', () => { + const o = { + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 10, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: new Uint8Array(0), + }; + assert.doesNotThrow(() => new algosdk.Transaction(o)); + }); + + it('should respect min tx fee', () => { + const o = { + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 0, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: new Uint8Array([123, 12, 200]), + }; + const txn = new algosdk.Transaction(o); + assert.strictEqual(txn.fee, 1000); // 1000 is the v5 min txn fee + const txnEnc = txn.get_obj_for_encoding(); + assert.strictEqual(txnEnc.fee, 1000); + }); + + it('should accept 0 fee', () => { + const o = { + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 0, + flatFee: true, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: new Uint8Array([123, 12, 200]), + }; + const txn = new algosdk.Transaction(o); + assert.equal(txn.fee, 0); + }); + + it('should accept lower than min fee', () => { + const o = { + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 10, + flatFee: true, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: new Uint8Array([123, 12, 200]), + }; + const txn = new algosdk.Transaction(o); + assert.equal(txn.fee, 10); + const txnEnc = txn.get_obj_for_encoding(); + assert.equal(txnEnc.fee, 10); + }); + + it('should not complain on a missing genesisID', () => { + const o = { + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 10, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: new Uint8Array([123, 12, 200]), + }; + + assert.doesNotThrow(() => new algosdk.Transaction(o)); + }); + + it('should not complain on an empty genesisID', () => { + const o = { + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 10, + amount: 847, + firstRound: 51, + lastRound: 61, + note: new Uint8Array([123, 12, 200]), + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + genesisID: '', + }; + + assert.doesNotThrow(() => new algosdk.Transaction(o)); + }); + + it('should complain if note isnt Uint8Array', () => { + const o = { + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 10, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: 'new Uint8Array(0)', + }; + assert.throws( + () => new algosdk.Transaction(o), + (err) => err.toString() === 'Error: note must be a Uint8Array.' + ); + }); + + it('should not drop a note of all zeros', () => { + const txnWithNote = new algosdk.Transaction({ + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 10, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: new Uint8Array(32), + }); + + const txnWithoutNote = new algosdk.Transaction({ + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 10, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + }); + + const serializedWithNote = algosdk.encodeUnsignedTransaction(txnWithNote); + const serializedWithoutNote = algosdk.encodeUnsignedTransaction( + txnWithoutNote + ); + + assert.notDeepStrictEqual(serializedWithNote, serializedWithoutNote); + }); + + it('should drop a lease of all zeros', () => { + const txnWithLease = new algosdk.Transaction({ + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 10, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + lease: new Uint8Array(32), + }); + + const txnWithoutLease = new algosdk.Transaction({ + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 10, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + }); + + const serializedWithLease = algosdk.encodeUnsignedTransaction(txnWithLease); + const serializedWithoutLease = algosdk.encodeUnsignedTransaction( + txnWithoutLease + ); + + assert.deepStrictEqual(serializedWithLease, serializedWithoutLease); + }); + + it('should drop an assetMetadataHash of all zeros', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + + const txnWithHash = new algosdk.Transaction({ + from: address, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetIndex: 1234, + assetManager: address, + assetReserve: address, + assetFreeze: address, + assetClawback: address, + type: 'acfg', + assetMetadataHash: new Uint8Array(32), + }); + + const txnWithoutHash = new algosdk.Transaction({ + from: address, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetIndex: 1234, + assetManager: address, + assetReserve: address, + assetFreeze: address, + assetClawback: address, + type: 'acfg', + }); + + const serializedWithHash = algosdk.encodeUnsignedTransaction(txnWithHash); + const serializedWithoutHash = algosdk.encodeUnsignedTransaction( + txnWithoutHash + ); + + assert.deepStrictEqual(serializedWithHash, serializedWithoutHash); + }); + + it('should be able to prettyprint and go toString without throwing', () => { + const o = { + from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + to: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + fee: 10, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: new Uint8Array(0), + }; + const txn = new algosdk.Transaction(o); + // assert package recommends just calling prettyPrint over using assert.doesNotThrow + txn.prettyPrint(); // should not throw + txn.toString(); // also should not throw + }); + + describe('should correctly serialize and deserialize from msgpack representation', () => { + it('should correctly serialize and deserialize from msgpack representation', () => { + const o = { + from: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + to: 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + fee: 10, + amount: 847, + firstRound: 51, + lastRound: 61, + note: new Uint8Array([123, 12, 200]), + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + genesisID: '', + }; + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize from msgpack representation with flat fee', () => { + const o = { + from: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + to: 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + fee: 2063, + amount: 847, + firstRound: 51, + lastRound: 61, + note: new Uint8Array([123, 12, 200]), + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + genesisID: '', + flatFee: true, + }; + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize a state proof transaction from msgpack representation', () => { + const o = { + from: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + fee: 10, + firstRound: 51, + lastRound: 61, + note: new Uint8Array([123, 12, 200]), + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + voteKey: '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE=', + selectionKey: 'oImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX4=', + voteFirst: 123, + voteLast: 456, + voteKeyDilution: 1234, + genesisID: '', + type: 'stpf', + stateProofType: 0, + stateProof: new Uint8Array([1, 1, 1, 1]), + stateProofMessage: new Uint8Array([0, 0, 0, 0]), + }; + const expectedTxn = new algosdk.Transaction(o); + console.log( + `${expectedTxn.stateProofType} ${expectedTxn.stateProofMessage} ${expectedTxn.stateProof} ${expectedTxn.type}` + ); + const encRep = expectedTxn.get_obj_for_encoding(); + console.log( + `${encRep.sptype} ${encRep.spmsg} ${encRep.sp} ${encRep.type}` + ); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize a key registration transaction from msgpack representation', () => { + const o = { + from: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + fee: 10, + firstRound: 51, + lastRound: 61, + note: new Uint8Array([123, 12, 200]), + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + voteKey: '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE=', + selectionKey: 'oImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX4=', + voteFirst: 123, + voteLast: 456, + voteKeyDilution: 1234, + genesisID: '', + type: 'keyreg', + }; + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an offline key registration transaction from msgpack representation', () => { + const o = { + from: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + fee: 10, + firstRound: 51, + lastRound: 61, + note: new Uint8Array([123, 12, 200]), + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + genesisID: '', + type: 'keyreg', + }; + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an offline key registration transaction from msgpack representation with explicit nonParticipation=false', () => { + const o = { + from: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + fee: 10, + firstRound: 51, + lastRound: 61, + note: new Uint8Array([123, 12, 200]), + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + genesisID: '', + nonParticipation: false, + type: 'keyreg', + }; + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize a nonparticipating key registration transaction from msgpack representation', () => { + const o = { + from: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + fee: 10, + firstRound: 51, + lastRound: 61, + note: new Uint8Array([123, 12, 200]), + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + nonParticipation: true, + genesisID: '', + type: 'keyreg', + }; + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an asset configuration transaction from msgpack representation', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const o = { + from: address, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetIndex: 1234, + assetManager: address, + assetReserve: address, + assetFreeze: address, + assetClawback: address, + type: 'acfg', + }; + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an asset creation transaction from msgpack representation', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const o = { + from: address, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetTotal: 1000, + assetDefaultFrozen: true, + assetUnitName: 'tests', + assetName: 'testcoin', + assetURL: 'testURL', + assetMetadataHash: new Uint8Array( + Buffer.from('ZkFDUE80blJnTzU1ajFuZEFLM1c2U2djNEFQa2N5Rmg=', 'base64') + ), + assetManager: address, + assetReserve: address, + assetFreeze: address, + assetClawback: address, + type: 'acfg', + }; + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an asset transfer transaction from msgpack representation', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const o = { + type: 'axfer', + from: address, + to: address, + amount: 100, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetIndex: 1234, + assetRevocationTarget: address, + closeRemainderTo: address, + }; + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an application create transaction from msgpack representation', () => { + const expectedTxn = algosdk.makeApplicationCreateTxnFromObject({ + from: 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4', + approvalProgram: Uint8Array.from([1, 32, 1, 1, 34]), + clearProgram: Uint8Array.from([2, 32, 1, 1, 34]), + numGlobalInts: 1, + numGlobalByteSlices: 2, + numLocalInts: 3, + numLocalByteSlices: 4, + onComplete: algosdk.OnApplicationComplete.OptInOC, + accounts: [ + 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + ], + appArgs: [Uint8Array.from([0]), Uint8Array.from([1, 2])], + extraPages: 2, + foreignApps: [3, 4], + foreignAssets: [5, 6], + boxes: [{ appIndex: 0, name: Uint8Array.from([0]) }], + lease: Uint8Array.from(new Array(32).fill(7)), + note: new Uint8Array(Buffer.from('note value')), + rekeyTo: 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + suggestedParams: { + fee: 0, + firstRound: 322575, + lastRound: 323575, + genesisID: 'testnet-v1.0', + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + }, + }); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an asset freeze transaction from msgpack representation', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const o = { + from: address, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + type: 'afrz', + freezeAccount: address, + assetIndex: 1, + freezeState: true, + }; + + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize a first round of 0', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const o = { + from: address, + fee: 10, + firstRound: 0, + lastRound: 1000, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + type: 'afrz', + freezeAccount: address, + assetIndex: 1, + freezeState: true, + }; + + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('reserializes correctly no genesis ID', () => { + const o = { + from: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + to: 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + fee: 10, + amount: 847, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: new Uint8Array([123, 12, 200]), + }; + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('reserializes correctly zero amount', () => { + const o = { + from: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + to: 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + fee: 10, + amount: 0, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: new Uint8Array([123, 12, 200]), + }; + const expectedTxn = new algosdk.Transaction(o); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize group object', () => { + const o = { + from: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + to: 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + fee: 10, + amount: 0, + firstRound: 51, + lastRound: 61, + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + note: new Uint8Array([123, 12, 200]), + }; + const tx = new algosdk.Transaction(o); + + { + const expectedTxg = new group.TxGroup([tx.rawTxID(), tx.rawTxID()]); + const encRep = expectedTxg.get_obj_for_encoding(); + const encTxg = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxg); + const decTxg = group.TxGroup.from_obj_for_encoding(decEncRep); + const reencRep = decTxg.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + } + + { + const expectedTxn = tx; + expectedTxn.group = tx.rawTxID(); + const encRep = expectedTxn.get_obj_for_encoding(); + const encTxn = algosdk.encodeObj(encRep); + const decEncRep = algosdk.decodeObj(encTxn); + const decTxn = algosdk.Transaction.from_obj_for_encoding(decEncRep); + const reencRep = decTxn.get_obj_for_encoding(); + assert.deepStrictEqual(reencRep, encRep); + } + }); + }); + + describe('transaction making functions', () => { + it('should be able to use helper to make a payment transaction', () => { + const from = 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const to = 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM'; + const fee = 10; + const amount = 847; + const firstRound = 51; + const lastRound = 61; + const note = new Uint8Array([123, 12, 200]); + const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const genesisID = ''; + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + let closeRemainderTo; + const o = { + from, + to, + fee, + amount, + closeRemainderTo, + firstRound, + lastRound, + note, + genesisHash, + genesisID, + reKeyTo: rekeyTo, + }; + const expectedTxn = new algosdk.Transaction(o); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const actualTxn = algosdk.makePaymentTxnWithSuggestedParams( + from, + to, + amount, + closeRemainderTo, + note, + suggestedParams, + rekeyTo + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + + it('should be able to use helper to make a payment transaction with BigInt amount', () => { + const from = 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const to = 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM'; + const fee = 10; + const amount = 0xffffffffffffffffn; + const firstRound = 51; + const lastRound = 61; + const note = new Uint8Array([123, 12, 200]); + const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const genesisID = ''; + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + let closeRemainderTo; + const o = { + from, + to, + fee, + amount, + closeRemainderTo, + firstRound, + lastRound, + note, + genesisHash, + genesisID, + reKeyTo: rekeyTo, + }; + const expectedTxn = new algosdk.Transaction(o); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const actualTxn = algosdk.makePaymentTxnWithSuggestedParams( + from, + to, + amount, + closeRemainderTo, + note, + suggestedParams, + rekeyTo + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + + it('should throw if payment amount is too large', () => { + const from = 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const to = 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM'; + const fee = 10; + const amount = 0x10000000000000000n; + const firstRound = 51; + const lastRound = 61; + const note = new Uint8Array([123, 12, 200]); + const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const genesisID = ''; + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + let closeRemainderTo; + const o = { + from, + to, + fee, + amount, + closeRemainderTo, + firstRound, + lastRound, + note, + genesisHash, + genesisID, + reKeyTo: rekeyTo, + }; + assert.throws( + () => new algosdk.Transaction(o), + new Error( + 'Amount must be a positive number and smaller than 2^64-1. If the number is larger than 2^53-1, use bigint.' + ) + ); + }); + + it('should be able to use helper to make a keyreg transaction', () => { + const from = 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const fee = 10; + const firstRound = 51; + const lastRound = 61; + const note = new Uint8Array([123, 12, 200]); + const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const genesisID = ''; + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const voteKey = '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE='; + const selectionKey = 'oImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX4='; + const voteKeyDilution = 1234; + const voteFirst = 123; + const voteLast = 456; + const o = { + from, + fee, + firstRound, + lastRound, + note, + genesisHash, + voteKey, + selectionKey, + voteFirst, + voteLast, + voteKeyDilution, + genesisID, + reKeyTo: rekeyTo, + type: 'keyreg', + }; + const expectedTxn = new algosdk.Transaction(o); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const actualTxn = algosdk.makeKeyRegistrationTxnWithSuggestedParams( + from, + note, + voteKey, + selectionKey, + voteFirst, + voteLast, + voteKeyDilution, + suggestedParams, + rekeyTo + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + + it('should be able to use helper to make an offline keyreg transaction', () => { + const from = 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const fee = 10; + const firstRound = 51; + const lastRound = 61; + const note = new Uint8Array([123, 12, 200]); + const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const genesisID = ''; + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const voteKey = undefined; + const selectionKey = undefined; + const voteKeyDilution = undefined; + const voteFirst = undefined; + const voteLast = undefined; + const o = { + from, + fee, + firstRound, + lastRound, + note, + genesisHash, + voteKey, + selectionKey, + voteFirst, + voteLast, + voteKeyDilution, + genesisID, + reKeyTo: rekeyTo, + type: 'keyreg', + nonParticipation: false, + }; + + assert.throws( + () => + new algosdk.Transaction({ + ...o, + voteKey: '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE=', + }), + new Error( + 'online key registration missing at least one of the following fields: ' + + 'voteKey, selectionKey, voteFirst, voteLast, voteKeyDilution' + ) + ); + + const expectedTxn = new algosdk.Transaction(o); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const actualTxn = algosdk.makeKeyRegistrationTxnWithSuggestedParams( + from, + note, + voteKey, + selectionKey, + voteFirst, + voteLast, + voteKeyDilution, + suggestedParams, + rekeyTo + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + + it('should be able to use helper to make a nonparticipating keyreg transaction', () => { + const from = 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const fee = 10; + const firstRound = 51; + const lastRound = 61; + const note = new Uint8Array([123, 12, 200]); + const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const genesisID = ''; + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const voteKey = '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE='; + const selectionKey = 'oImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX4='; + const voteKeyDilution = 1234; + const voteFirst = 123; + const voteLast = 456; + const nonParticipation = true; + const o = { + from, + fee, + firstRound, + lastRound, + note, + genesisHash, + nonParticipation, + genesisID, + reKeyTo: rekeyTo, + type: 'keyreg', + }; + + assert.throws( + () => + new algosdk.Transaction({ + ...o, + voteKey, + selectionKey, + voteFirst, + voteLast, + voteKeyDilution, + }), + new Error( + 'nonParticipation is true but participation params are present.' + ) + ); + + const expectedTxn = new algosdk.Transaction(o); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const actualTxn = algosdk.makeKeyRegistrationTxnWithSuggestedParams( + from, + note, + undefined, + undefined, + undefined, + undefined, + undefined, + suggestedParams, + rekeyTo, + nonParticipation + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + + it('should be able to use helper to make an asset create transaction', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const fee = 10; + const defaultFrozen = false; + const genesisHash = 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI='; + const total = 100; + const decimals = 0; + const reserve = addr; + const freeze = addr; + const clawback = addr; + const unitName = 'tst'; + const assetName = 'testcoin'; + const assetURL = 'testURL'; + const assetMetadataHash = new Uint8Array( + Buffer.from('dGVzdGhhc2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', 'base64') + ); + const genesisID = ''; + const firstRound = 322575; + const lastRound = 322575; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const o = { + from: addr, + fee, + firstRound, + lastRound, + note, + genesisHash, + assetTotal: total, + assetDecimals: decimals, + assetDefaultFrozen: defaultFrozen, + assetUnitName: unitName, + assetName, + assetURL, + assetMetadataHash, + assetManager: addr, + assetReserve: reserve, + assetFreeze: freeze, + assetClawback: clawback, + genesisID, + reKeyTo: rekeyTo, + type: 'acfg', + }; + const expectedTxn = new algosdk.Transaction(o); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const actualTxn = algosdk.makeAssetCreateTxnWithSuggestedParams( + addr, + note, + total, + decimals, + defaultFrozen, + addr, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + suggestedParams, + rekeyTo + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + + it('should be able to use helper to make an asset create transaction with BigInt total', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const fee = 10; + const defaultFrozen = false; + const genesisHash = 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI='; + const total = 0xffffffffffffffffn; + const decimals = 0; + const reserve = addr; + const freeze = addr; + const clawback = addr; + const unitName = 'tst'; + const assetName = 'testcoin'; + const assetURL = 'testURL'; + const assetMetadataHash = new Uint8Array( + Buffer.from('dGVzdGhhc2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', 'base64') + ); + const genesisID = ''; + const firstRound = 322575; + const lastRound = 322575; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const o = { + from: addr, + fee, + firstRound, + lastRound, + note, + genesisHash, + assetTotal: total, + assetDecimals: decimals, + assetDefaultFrozen: defaultFrozen, + assetUnitName: unitName, + assetName, + assetURL, + assetMetadataHash, + assetManager: addr, + assetReserve: reserve, + assetFreeze: freeze, + assetClawback: clawback, + genesisID, + reKeyTo: rekeyTo, + type: 'acfg', + }; + const expectedTxn = new algosdk.Transaction(o); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const actualTxn = algosdk.makeAssetCreateTxnWithSuggestedParams( + addr, + note, + total, + decimals, + defaultFrozen, + addr, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + suggestedParams, + rekeyTo + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + + it('should throw if asset creation total is too large', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const fee = 10; + const defaultFrozen = false; + const genesisHash = 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI='; + const total = 0x10000000000000000n; + const decimals = 0; + const reserve = addr; + const freeze = addr; + const clawback = addr; + const unitName = 'tst'; + const assetName = 'testcoin'; + const assetURL = 'testURL'; + const assetMetadataHash = new Uint8Array( + Buffer.from('dGVzdGhhc2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', 'base64') + ); + const genesisID = ''; + const firstRound = 322575; + const lastRound = 322575; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const o = { + from: addr, + fee, + firstRound, + lastRound, + note, + genesisHash, + assetTotal: total, + assetDecimals: decimals, + assetDefaultFrozen: defaultFrozen, + assetUnitName: unitName, + assetName, + assetURL, + assetMetadataHash, + assetManager: addr, + assetReserve: reserve, + assetFreeze: freeze, + assetClawback: clawback, + genesisID, + reKeyTo: rekeyTo, + type: 'acfg', + }; + assert.throws( + () => new algosdk.Transaction(o), + new Error( + 'Total asset issuance must be a positive number and smaller than 2^64-1. If the number is larger than 2^53-1, use bigint.' + ) + ); + }); + + it('should fail to make an asset create transaction with an invalid assetMetadataHash', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const fee = 10; + const defaultFrozen = false; + const genesisHash = 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI='; + const total = 100; + const decimals = 0; + const reserve = addr; + const freeze = addr; + const clawback = addr; + const unitName = 'tst'; + const assetName = 'testcoin'; + const assetURL = 'testURL'; + const genesisID = ''; + const firstRound = 322575; + const lastRound = 322575; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const txnTemplate = { + from: addr, + fee, + firstRound, + lastRound, + note, + genesisHash, + assetTotal: total, + assetDecimals: decimals, + assetDefaultFrozen: defaultFrozen, + assetUnitName: unitName, + assetName, + assetURL, + assetManager: addr, + assetReserve: reserve, + assetFreeze: freeze, + assetClawback: clawback, + genesisID, + reKeyTo: rekeyTo, + type: 'acfg', + }; + assert.doesNotThrow(() => { + const txnParams = { + assetMetadataHash: '', + ...txnTemplate, + }; + return new algosdk.Transaction(txnParams); + }); + assert.throws(() => { + const txnParams = { + assetMetadataHash: 'abc', + ...txnTemplate, + }; + return new algosdk.Transaction(txnParams); + }); + assert.doesNotThrow(() => { + const txnParams = { + assetMetadataHash: 'fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh', + ...txnTemplate, + }; + return new algosdk.Transaction(txnParams); + }); + assert.throws(() => { + const txnParams = { + assetMetadataHash: 'fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh1', + ...txnTemplate, + }; + return new algosdk.Transaction(txnParams); + }); + assert.doesNotThrow(() => { + const txnParams = { + assetMetadataHash: new Uint8Array(0), + ...txnTemplate, + }; + return new algosdk.Transaction(txnParams); + }); + assert.throws(() => { + const txnParams = { + assetMetadataHash: new Uint8Array([1, 2, 3]), + ...txnTemplate, + }; + return new algosdk.Transaction(txnParams); + }); + assert.doesNotThrow(() => { + const txnParams = { + assetMetadataHash: new Uint8Array(32), + ...txnTemplate, + }; + return new algosdk.Transaction(txnParams); + }); + assert.throws(() => { + const txnParams = { + assetMetadataHash: new Uint8Array(33), + ...txnTemplate, + }; + return new algosdk.Transaction(txnParams); + }); + }); + + it('should be able to use helper to make an asset config transaction', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const fee = 10; + const assetIndex = 1234; + const genesisHash = 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI='; + const manager = addr; + const reserve = addr; + const freeze = addr; + const clawback = addr; + const genesisID = ''; + const firstRound = 322575; + const lastRound = 322575; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const o = { + from: addr, + fee, + firstRound, + lastRound, + genesisHash, + genesisID, + assetIndex, + assetManager: manager, + assetReserve: reserve, + assetFreeze: freeze, + assetClawback: clawback, + type: 'acfg', + note, + reKeyTo: rekeyTo, + }; + const expectedTxn = new algosdk.Transaction(o); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const actualTxn = algosdk.makeAssetConfigTxnWithSuggestedParams( + addr, + note, + assetIndex, + manager, + reserve, + freeze, + clawback, + suggestedParams, + true, + rekeyTo + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + + it('should throw when disobeying strict address checking in make asset config', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const fee = 10; + const assetIndex = 1234; + const genesisHash = 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI='; + const manager = addr; + let reserve; + let freeze; + const clawback = addr; + const genesisID = ''; + const firstRound = 322575; + const lastRound = 322575; + const note = new Uint8Array([123, 12, 200]); + let threw = false; + try { + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + algosdk.makeAssetConfigTxnWithSuggestedParams( + addr, + note, + assetIndex, + manager, + reserve, + freeze, + clawback, + suggestedParams + ); + } catch { + threw = true; + } + assert.deepStrictEqual(true, threw); + }); + + it('should be able to use helper to make an asset destroy transaction', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const fee = 10; + const assetIndex = 1234; + const genesisHash = 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI='; + const genesisID = ''; + const firstRound = 322575; + const lastRound = 322575; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const o = { + from: addr, + fee, + firstRound, + lastRound, + genesisHash, + genesisID, + assetIndex, + type: 'acfg', + note, + reKeyTo: rekeyTo, + }; + const expectedTxn = new algosdk.Transaction(o); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const actualTxn = algosdk.makeAssetDestroyTxnWithSuggestedParams( + addr, + note, + assetIndex, + suggestedParams, + rekeyTo + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + + it('should be able to use helper to make an asset transfer transaction', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const fee = 10; + const sender = addr; + const recipient = addr; + const revocationTarget = addr; + const closeRemainderTo = addr; + const assetIndex = 1234; + const amount = 100; + const genesisHash = 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI='; + const genesisID = ''; + const firstRound = 322575; + const lastRound = 322575; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const o = { + type: 'axfer', + from: sender, + to: recipient, + amount, + fee, + firstRound, + lastRound, + genesisHash, + genesisID, + assetIndex, + note, + assetRevocationTarget: revocationTarget, + closeRemainderTo, + reKeyTo: rekeyTo, + }; + const expectedTxn = new algosdk.Transaction(o); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + + const actualTxn = algosdk.makeAssetTransferTxnWithSuggestedParams( + sender, + recipient, + closeRemainderTo, + revocationTarget, + amount, + note, + assetIndex, + suggestedParams, + rekeyTo + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + + it('should be able to use helper to make an asset freeze transaction', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const fee = 10; + const assetIndex = 1234; + const genesisHash = 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI='; + const freezeTarget = addr; + const genesisID = ''; + const firstRound = 322575; + const lastRound = 322575; + const freezeState = true; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const o = { + from: addr, + fee, + firstRound, + lastRound, + genesisHash, + type: 'afrz', + freezeAccount: freezeTarget, + assetIndex, + freezeState, + note, + genesisID, + reKeyTo: rekeyTo, + }; + const expectedTxn = new algosdk.Transaction(o); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + + const actualTxn = algosdk.makeAssetFreezeTxnWithSuggestedParams( + addr, + note, + assetIndex, + freezeTarget, + freezeState, + suggestedParams, + rekeyTo + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + it('should be able to use helper to assign group ID to mixed Transaction and Dict', () => { + const suggestedParams = { + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + genesisID: '', + firstRound: 322575, + lastRound: 322575 + 1000, + fee: 1000, + flatFee: true, + }; + + const helperTx = algosdk.makePaymentTxnWithSuggestedParams( + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM', + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM', + 1000, + undefined, + new Uint8Array(0), + suggestedParams + ); + + const dictTx = { + from: 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM', + to: 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM', + fee: 1000, + flatFee: true, + amount: 0, + firstRound: 322575, + lastRound: 322575 + 1000, + genesisID: '', + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + type: 'pay', + }; + + // Store both transactions + const txns = [helperTx, dictTx]; + + // Group both transactions + const txgroup = algosdk.assignGroupID(txns); + + assert.deepStrictEqual(txgroup[0].group, txgroup[1].group); + }); + it('should be able to translate box references to encoded references', () => { + const testCases = [ + [ + [{ appIndex: 100, name: [0, 1, 2, 3] }], + [100], + 9999, + [{ i: 1, n: [0, 1, 2, 3] }], + ], + [[], [], 9999, []], + [ + [ + { appIndex: 0, name: [0, 1, 2, 3] }, + { appIndex: 9999, name: [4, 5, 6, 7] }, + ], + [100], + 9999, + [ + { i: 0, n: [0, 1, 2, 3] }, + { i: 0, n: [4, 5, 6, 7] }, + ], + ], + [ + [{ appIndex: 100, name: [0, 1, 2, 3] }], + [100], + 100, + [{ i: 1, n: [0, 1, 2, 3] }], + ], + [ + [ + { appIndex: 7777, name: [0, 1, 2, 3] }, + { appIndex: 8888, name: [4, 5, 6, 7] }, + ], + [100, 7777, 8888, 9999], + 9999, + [ + { i: 2, n: [0, 1, 2, 3] }, + { i: 3, n: [4, 5, 6, 7] }, + ], + ], + ]; + for (const testCase of testCases) { + const expected = testCase[3]; + const actual = translateBoxReferences( + testCase[0], + testCase[1], + testCase[2] + ); + assert.deepStrictEqual(expected, actual); + } + }); + }); +}); diff --git a/tests/6.Multisig.ts b/tests/6.Multisig.ts new file mode 100644 index 0000000..a2577e9 --- /dev/null +++ b/tests/6.Multisig.ts @@ -0,0 +1,747 @@ +/* eslint-env mocha */ +import { Buffer } from 'buffer'; +import assert from 'assert'; +import algosdk from '../src/index'; +import { + MultisigTransaction, + MULTISIG_NO_MUTATE_ERROR_MSG, + MULTISIG_USE_PARTIAL_SIGN_ERROR_MSG, + MULTISIG_SIGNATURE_LENGTH_ERROR_MSG, +} from '../src/multisig'; + +const sampleAccount1 = algosdk.mnemonicToSecretKey( + 'auction inquiry lava second expand liberty glass involve ginger illness length room item discover ahead table doctor term tackle cement bonus profit right above catch' +); +const sampleAccount2 = algosdk.mnemonicToSecretKey( + 'since during average anxiety protect cherry club long lawsuit loan expand embark forum theory winter park twenty ball kangaroo cram burst board host ability left' +); +const sampleAccount3 = algosdk.mnemonicToSecretKey( + 'advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor' +); + +// Multisig Golden Params +const sampleMultisigParams: algosdk.MultisigMetadata = { + version: 1, + threshold: 2, + addrs: [sampleAccount1.addr, sampleAccount2.addr, sampleAccount3.addr], +}; + +const sampleMultisigAddr = algosdk.multisigAddress(sampleMultisigParams); + +describe('Sample Multisig Info', () => { + it('is correct', () => { + assert.strictEqual( + sampleAccount1.addr, + 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA' + ); + assert.strictEqual( + sampleAccount2.addr, + 'BFRTECKTOOE7A5LHCF3TTEOH2A7BW46IYT2SX5VP6ANKEXHZYJY77SJTVM' + ); + assert.strictEqual( + sampleAccount3.addr, + '47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU' + ); + assert.strictEqual( + sampleMultisigAddr, + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + ); + }); +}); + +describe('Multisig Functionality', () => { + describe('signMultisigTransaction', () => { + it('should match golden main repo result', () => { + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: sampleMultisigAddr, + to: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', + amount: 1000, + note: new Uint8Array(Buffer.from('RSYiABhShvs=', 'base64')), + closeRemainderTo: + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA', + suggestedParams: { + fee: 1000, + flatFee: true, + firstRound: 62229, + lastRound: 63229, + genesisID: 'devnet-v38.0', + genesisHash: '/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp0=', + }, + }); + + const { txID, blob } = algosdk.signMultisigTransaction( + txn, + sampleMultisigParams, + sampleAccount1.sk + ); + + const expectedTxID = + 'MANN3ESOHQVHFZBAGD6UK6XFVWEFZQJPWO5SQ2J5LZRCF5E2VVQQ'; + assert.strictEqual(txID, expectedTxID); + + const expectedSignedTxn = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==', + 'base64' + ); + assert.deepStrictEqual(Buffer.from(blob), expectedSignedTxn); + }); + + it('should correctly handle a different sender', () => { + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: 'EHGMQCXBIFBE364DEKWQVVNCTCTVCGQL3BR2Q5I7CFTRXWIVTF4SYA3GHU', + to: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', + amount: 1000, + note: new Uint8Array(Buffer.from('RSYiABhShvs=', 'base64')), + closeRemainderTo: + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA', + suggestedParams: { + fee: 1000, + flatFee: true, + firstRound: 62229, + lastRound: 63229, + genesisID: 'devnet-v38.0', + genesisHash: '/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp0=', + }, + }); + + const { txID, blob } = algosdk.signMultisigTransaction( + txn, + sampleMultisigParams, + sampleAccount1.sk + ); + + const expectedTxID = + 'YQOXQNNO56WXQSU3IDUM2C4J7IZI6WMSCMKN5UCP5ZK6GWA6BXKQ'; + assert.strictEqual(txID, expectedTxID); + + const expectedSignedTxn = Buffer.from( + 'g6Rtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAWLcyn6nB68yo/PUHXB21wyTy+PhHgMQNOIXPTGB96faZP2xqpQ8IFlIR2LPotlX68ylK8MCl82SUfMR4FYyzAIGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaRzZ25yxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCAhzMgK4UFCTfuDIq0K1aKYp1EaC9hjqHUfEWcb2RWZeaR0eXBlo3BheQ==', + 'base64' + ); + assert.deepStrictEqual(Buffer.from(blob), expectedSignedTxn); + }); + }); + + describe('appendSignMultisigTransaction', () => { + it('should match golden main repo result', () => { + const oneSigTxn = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==', + 'base64' + ); + + const { txID, blob } = algosdk.appendSignMultisigTransaction( + oneSigTxn, + sampleMultisigParams, + sampleAccount2.sk + ); + + const expectedTxID = + 'MANN3ESOHQVHFZBAGD6UK6XFVWEFZQJPWO5SQ2J5LZRCF5E2VVQQ'; + assert.strictEqual(txID, expectedTxID); + + const expectedBlob = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQBAhuyRjsOrnHp3s/xI+iMKiL7QPsh8iJZ22YOJJP0aFUwedMr+a6wfdBXk1OefyrAN1wqJ9rq6O+DrWV1fH0ASBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNA+iiZnbN8xWjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbN9v2kbm90ZcQIRSYiABhShvujcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWjcGF5', + 'base64' + ); + assert.deepStrictEqual(Buffer.from(blob), expectedBlob); + }); + + it('should correctly handle a different sender', () => { + const oneSigTxn = Buffer.from( + 'g6Rtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAWLcyn6nB68yo/PUHXB21wyTy+PhHgMQNOIXPTGB96faZP2xqpQ8IFlIR2LPotlX68ylK8MCl82SUfMR4FYyzAIGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaRzZ25yxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCAhzMgK4UFCTfuDIq0K1aKYp1EaC9hjqHUfEWcb2RWZeaR0eXBlo3BheQ==', + 'base64' + ); + + const { txID, blob } = algosdk.appendSignMultisigTransaction( + oneSigTxn, + sampleMultisigParams, + sampleAccount2.sk + ); + + const expectedTxID = + 'YQOXQNNO56WXQSU3IDUM2C4J7IZI6WMSCMKN5UCP5ZK6GWA6BXKQ'; + assert.strictEqual(txID, expectedTxID); + + const expectedBlob = Buffer.from( + 'g6Rtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAWLcyn6nB68yo/PUHXB21wyTy+PhHgMQNOIXPTGB96faZP2xqpQ8IFlIR2LPotlX68ylK8MCl82SUfMR4FYyzAIKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQPuH2WlM2x1tflA6LGMhKXBiuO7dsRzjXYPgcfjvDth9ZsCazyOKHqQmtYjD+pXLM8fJbrRhoUGTkyLxINiT9wCBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBpHNnbnLEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfpo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNA+iiZnbN8xWjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbN9v2kbm90ZcQIRSYiABhShvujcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEICHMyArhQUJN+4MirQrVopinURoL2GOodR8RZxvZFZl5pHR5cGWjcGF5', + 'base64' + ); + assert.deepStrictEqual(Buffer.from(blob), expectedBlob); + }); + }); + + describe('create/append multisig with external signatures', () => { + it('should match golden main repo result', () => { + const oneSigTxn = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==', + 'base64' + ); + + const signerAddr = sampleAccount2.addr; + const signedTxn = algosdk.appendSignMultisigTransaction( + oneSigTxn, + sampleMultisigParams, + sampleAccount2.sk + ); + + const multisig = algosdk.decodeSignedTransaction(signedTxn.blob).msig; + if (multisig === undefined) { + throw new Error('multisig is undefined'); + } + + const signatures = multisig.subsig; + if (signatures === undefined) { + throw new Error('No signatures found'); + } + + const signature = signatures[1].s; + if (signature === undefined) { + throw new Error('No signature found'); + } + + const { txID, blob } = algosdk.appendSignRawMultisigSignature( + oneSigTxn, + sampleMultisigParams, + signerAddr, + signature + ); + + const expectedTxID = + 'MANN3ESOHQVHFZBAGD6UK6XFVWEFZQJPWO5SQ2J5LZRCF5E2VVQQ'; + assert.strictEqual(txID, expectedTxID); + + const expectedBlob = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQBAhuyRjsOrnHp3s/xI+iMKiL7QPsh8iJZ22YOJJP0aFUwedMr+a6wfdBXk1OefyrAN1wqJ9rq6O+DrWV1fH0ASBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNA+iiZnbN8xWjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbN9v2kbm90ZcQIRSYiABhShvujcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWjcGF5', + 'base64' + ); + + assert.deepStrictEqual(Buffer.from(blob), expectedBlob); + }); + + it('should not sign with signature of invalid length', () => { + const oneSigTxn = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==', + 'base64' + ); + + const signerAddr = sampleAccount2.addr; + const signedTxn = algosdk.appendSignMultisigTransaction( + oneSigTxn, + sampleMultisigParams, + sampleAccount2.sk + ); + + const multisig = algosdk.decodeSignedTransaction(signedTxn.blob).msig; + if (multisig === undefined) { + throw new Error('multisig is undefined'); + } + + const signatures = multisig.subsig; + if (signatures === undefined) { + throw new Error('No signatures found'); + } + + const signature = signatures[1].s; + if (signature === undefined) { + throw new Error('No signature found'); + } + + // Remove the last byte of the signature + const invalidSignature = signature.slice(0, -1); + assert.throws( + () => + algosdk.appendSignRawMultisigSignature( + oneSigTxn, + sampleMultisigParams, + signerAddr, + invalidSignature + ), + Error(MULTISIG_SIGNATURE_LENGTH_ERROR_MSG) + ); + }); + + it('should append signature to created raw multisig transaction', () => { + const rawTxBlob = Buffer.from( + 'jKNmZWXOAAPIwKJmds4ADvnao2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zgAO/cKmc2Vsa2V5xCAyEisr1j3cUzGWF6WqU8Sxwm/j3MryjTYitWl3oUBchqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWma2V5cmVnp3ZvdGVmc3TOAA27oKZ2b3Rla2TNJxCndm90ZWtlecQgcBvX+5ErB7MIEf8oHZ/ulWPlgC4gJokjGSWPd/qTHoindm90ZWxzdM4AD0JA', + 'base64' + ); + const decRawTx = algosdk.decodeUnsignedTransaction(rawTxBlob); + + const unsignedMultisigTx = algosdk.createMultisigTransaction( + decRawTx, + sampleMultisigParams + ); + + // Check that the unsignedMultisigTx is valid + interface ExpectedMultisigTxStructure { + msig: { + subsig: { + pk: Uint8Array; + s: Uint8Array; + }[]; + }; + } + const unsignedMultisigTxBlob = algosdk.decodeObj( + unsignedMultisigTx + ) as ExpectedMultisigTxStructure; + assert.deepStrictEqual( + unsignedMultisigTxBlob.msig.subsig[0].pk, + algosdk.decodeAddress(sampleAccount1.addr).publicKey + ); + assert.strictEqual(unsignedMultisigTxBlob.msig.subsig[1].s, undefined); + + // Sign the raw transaction with a signature generated from the first account + const signerAddr = sampleAccount1.addr; + const signedTxn = algosdk.appendSignMultisigTransaction( + unsignedMultisigTx, + sampleMultisigParams, + sampleAccount1.sk + ); + + const multisig = algosdk.decodeSignedTransaction(signedTxn.blob).msig; + if (multisig === undefined) { + throw new Error('multisig is undefined'); + } + + const signatures = multisig.subsig; + if (signatures === undefined) { + throw new Error('No signatures found'); + } + + const signature = signatures[0].s; + if (signature === undefined) { + throw new Error('No signature found'); + } + const { txID, blob } = algosdk.appendSignRawMultisigSignature( + unsignedMultisigTx, + sampleMultisigParams, + signerAddr, + signature + ); + + // Check that the signed raw multisig is valid + const expectedTxID = + 'E7DA7WTJCWWFQMKSVU5HOIJ5F5HGVGMOZGBIHRJRYGIX7FIJ5VWA'; + assert.strictEqual(txID, expectedTxID); + + const expectedBlob = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAcT0s17wJbvnza+NpyHwM0RWbQ+HwKmsT1PLs+w6d6MpdTH3tra+yKZE0K0qEyhSE7Y56+B9oaf2orEbjc/njDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Mo2ZlZc4AA8jAomZ2zgAO+dqjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbOAA79wqZzZWxrZXnEIDISKyvWPdxTMZYXpapTxLHCb+PcyvKNNiK1aXehQFyGo3NuZMQgjZK0iZABc6BN+kNZo2ZqavzqLEKgXdnB9z7rpUeAN+mkdHlwZaZrZXlyZWendm90ZWZzdM4ADbugpnZvdGVrZM0nEKd2b3Rla2V5xCBwG9f7kSsHswgR/ygdn+6VY+WALiAmiSMZJY93+pMeiKd2b3RlbHN0zgAPQkA=', + 'base64' + ); + + assert.deepStrictEqual(Buffer.from(blob), expectedBlob); + }); + }); + + describe('should sign keyreg transaction types', () => { + it('first partial sig should match golden main repo result', () => { + const rawTxBlob = Buffer.from( + 'jKNmZWXOAAPIwKJmds4ADvnao2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zgAO/cKmc2Vsa2V5xCAyEisr1j3cUzGWF6WqU8Sxwm/j3MryjTYitWl3oUBchqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWma2V5cmVnp3ZvdGVmc3TOAA27oKZ2b3Rla2TNJxCndm90ZWtlecQgcBvX+5ErB7MIEf8oHZ/ulWPlgC4gJokjGSWPd/qTHoindm90ZWxzdM4AD0JA', + 'base64' + ); + const decRawTx = algosdk.decodeUnsignedTransaction(rawTxBlob); + + const { txID, blob } = algosdk.signMultisigTransaction( + decRawTx, + sampleMultisigParams, + sampleAccount1.sk + ); + + const expectedTxID = + 'E7DA7WTJCWWFQMKSVU5HOIJ5F5HGVGMOZGBIHRJRYGIX7FIJ5VWA'; + assert.strictEqual(txID, expectedTxID); + + const oneSigTxBlob = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAcT0s17wJbvnza+NpyHwM0RWbQ+HwKmsT1PLs+w6d6MpdTH3tra+yKZE0K0qEyhSE7Y56+B9oaf2orEbjc/njDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Mo2ZlZc4AA8jAomZ2zgAO+dqjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbOAA79wqZzZWxrZXnEIDISKyvWPdxTMZYXpapTxLHCb+PcyvKNNiK1aXehQFyGo3NuZMQgjZK0iZABc6BN+kNZo2ZqavzqLEKgXdnB9z7rpUeAN+mkdHlwZaZrZXlyZWendm90ZWZzdM4ADbugpnZvdGVrZM0nEKd2b3Rla2V5xCBwG9f7kSsHswgR/ygdn+6VY+WALiAmiSMZJY93+pMeiKd2b3RlbHN0zgAPQkA=', + 'base64' + ); + + assert.deepStrictEqual(Buffer.from(blob), oneSigTxBlob); + }); + + it('second partial sig with 3rd pk should match golden main repo result', () => { + const rawOneSigTxBlob = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAcT0s17wJbvnza+NpyHwM0RWbQ+HwKmsT1PLs+w6d6MpdTH3tra+yKZE0K0qEyhSE7Y56+B9oaf2orEbjc/njDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Mo2ZlZc4AA8jAomZ2zgAO+dqjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbOAA79wqZzZWxrZXnEIDISKyvWPdxTMZYXpapTxLHCb+PcyvKNNiK1aXehQFyGo3NuZMQgjZK0iZABc6BN+kNZo2ZqavzqLEKgXdnB9z7rpUeAN+mkdHlwZaZrZXlyZWendm90ZWZzdM4ADbugpnZvdGVrZM0nEKd2b3Rla2V5xCBwG9f7kSsHswgR/ygdn+6VY+WALiAmiSMZJY93+pMeiKd2b3RlbHN0zgAPQkA=', + 'base64' + ); + + const decRawTx = algosdk.decodeSignedTransaction(rawOneSigTxBlob).txn; + + const { txID, blob } = algosdk.signMultisigTransaction( + decRawTx, + sampleMultisigParams, + sampleAccount3.sk + ); + + const expectedTxID = + 'E7DA7WTJCWWFQMKSVU5HOIJ5F5HGVGMOZGBIHRJRYGIX7FIJ5VWA'; + assert.strictEqual(txID, expectedTxID); + + const finMsigBlob = algosdk.mergeMultisigTransactions([ + blob, + new Uint8Array(rawOneSigTxBlob), + ]); + + const twoSigTxBlob = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAcT0s17wJbvnza+NpyHwM0RWbQ+HwKmsT1PLs+w6d6MpdTH3tra+yKZE0K0qEyhSE7Y56+B9oaf2orEbjc/njDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgqJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGhc8RAfScXfzBLaE42NbF9IR0qMa3NBQnhY6kQsV+6htqBsw/bWmc2vBla65CJLSNCNS236BsUMlvbwrs3knF+6bqhD6N0aHICoXYBo3R4boyjZmVlzgADyMCiZnbOAA752qNnZW6sZGV2bmV0LXYzOC4womdoxCD+s2w5EBQ5AMPaVULKGDawD9L4GVkSV80j9gQvmMg2naJsds4ADv3CpnNlbGtlecQgMhIrK9Y93FMxlhelqlPEscJv49zK8o02IrVpd6FAXIajc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlpmtleXJlZ6d2b3RlZnN0zgANu6Cmdm90ZWtkzScQp3ZvdGVrZXnEIHAb1/uRKwezCBH/KB2f7pVj5YAuICaJIxklj3f6kx6Ip3ZvdGVsc3TOAA9CQA==', + 'base64' + ); + + assert.deepStrictEqual(Buffer.from(finMsigBlob), twoSigTxBlob); + }); + }); + + describe('mergeMultisigTransactions', () => { + it('should be symmetric and match golden main repo result', () => { + // prettier-ignore + const oneAndThreeBlob = Buffer.from([130, 164, 109, 115, 105, 103, 131, 166, 115, 117, 98, 115, 105, 103, 147, 130, 162, 112, 107, 196, 32, 27, 126, 192, 176, 75, 234, 97, 183, 150, 144, 151, 230, 203, 244, 7, 225, 8, 167, 5, 53, 29, 11, 201, 138, 190, 177, 34, 9, 168, 171, 129, 120, 161, 115, 196, 64, 113, 61, 44, 215, 188, 9, 110, 249, 243, 107, 227, 105, 200, 124, 12, 209, 21, 155, 67, 225, 240, 42, 107, 19, 212, 242, 236, 251, 14, 157, 232, 202, 93, 76, 125, 237, 173, 175, 178, 41, 145, 52, 43, 74, 132, 202, 20, 132, 237, 142, 122, 248, 31, 104, 105, 253, 168, 172, 70, 227, 115, 249, 227, 13, 129, 162, 112, 107, 196, 32, 9, 99, 50, 9, 83, 115, 137, 240, 117, 103, 17, 119, 57, 145, 199, 208, 62, 27, 115, 200, 196, 245, 43, 246, 175, 240, 26, 162, 92, 249, 194, 113, 130, 162, 112, 107, 196, 32, 231, 240, 248, 77, 6, 129, 29, 249, 243, 28, 141, 135, 139, 17, 85, 244, 103, 29, 81, 161, 133, 194, 0, 144, 134, 103, 244, 73, 88, 112, 104, 161, 161, 115, 196, 64, 125, 39, 23, 127, 48, 75, 104, 78, 54, 53, 177, 125, 33, 29, 42, 49, 173, 205, 5, 9, 225, 99, 169, 16, 177, 95, 186, 134, 218, 129, 179, 15, 219, 90, 103, 54, 188, 25, 90, 235, 144, 137, 45, 35, 66, 53, 45, 183, 232, 27, 20, 50, 91, 219, 194, 187, 55, 146, 113, 126, 233, 186, 161, 15, 163, 116, 104, 114, 2, 161, 118, 1, 163, 116, 120, 110, 140, 163, 102, 101, 101, 206, 0, 3, 200, 192, 162, 102, 118, 206, 0, 14, 249, 218, 163, 103, 101, 110, 172, 100, 101, 118, 110, 101, 116, 45, 118, 51, 56, 46, 48, 162, 103, 104, 196, 32, 254, 179, 108, 57, 16, 20, 57, 0, 195, 218, 85, 66, 202, 24, 54, 176, 15, 210, 248, 25, 89, 18, 87, 205, 35, 246, 4, 47, 152, 200, 54, 157, 162, 108, 118, 206, 0, 14, 253, 194, 166, 115, 101, 108, 107, 101, 121, 196, 32, 50, 18, 43, 43, 214, 61, 220, 83, 49, 150, 23, 165, 170, 83, 196, 177, 194, 111, 227, 220, 202, 242, 141, 54, 34, 181, 105, 119, 161, 64, 92, 134, 163, 115, 110, 100, 196, 32, 141, 146, 180, 137, 144, 1, 115, 160, 77, 250, 67, 89, 163, 102, 106, 106, 252, 234, 44, 66, 160, 93, 217, 193, 247, 62, 235, 165, 71, 128, 55, 233, 164, 116, 121, 112, 101, 166, 107, 101, 121, 114, 101, 103, 167, 118, 111, 116, 101, 102, 115, 116, 206, 0, 13, 187, 160, 166, 118, 111, 116, 101, 107, 100, 205, 39, 16, 167, 118, 111, 116, 101, 107, 101, 121, 196, 32, 112, 27, 215, 251, 145, 43, 7, 179, 8, 17, 255, 40, 29, 159, 238, 149, 99, 229, 128, 46, 32, 38, 137, 35, 25, 37, 143, 119, 250, 147, 30, 136, 167, 118, 111, 116, 101, 108, 115, 116, 206, 0, 15, 66, 64]); + // prettier-ignore + const twoAndThreeBlob = Buffer.from([130, 164, 109, 115, 105, 103, 131, 166, 115, 117, 98, 115, 105, 103, 147, 129, 162, 112, 107, 196, 32, 27, 126, 192, 176, 75, 234, 97, 183, 150, 144, 151, 230, 203, 244, 7, 225, 8, 167, 5, 53, 29, 11, 201, 138, 190, 177, 34, 9, 168, 171, 129, 120, 130, 162, 112, 107, 196, 32, 9, 99, 50, 9, 83, 115, 137, 240, 117, 103, 17, 119, 57, 145, 199, 208, 62, 27, 115, 200, 196, 245, 43, 246, 175, 240, 26, 162, 92, 249, 194, 113, 161, 115, 196, 64, 227, 199, 17, 26, 50, 149, 36, 250, 241, 222, 56, 188, 127, 140, 131, 144, 167, 224, 18, 230, 61, 37, 113, 136, 156, 116, 104, 237, 140, 138, 121, 215, 140, 159, 38, 64, 106, 111, 177, 108, 51, 233, 152, 250, 233, 207, 138, 116, 61, 55, 89, 204, 6, 164, 2, 114, 128, 230, 199, 130, 136, 25, 207, 1, 130, 162, 112, 107, 196, 32, 231, 240, 248, 77, 6, 129, 29, 249, 243, 28, 141, 135, 139, 17, 85, 244, 103, 29, 81, 161, 133, 194, 0, 144, 134, 103, 244, 73, 88, 112, 104, 161, 161, 115, 196, 64, 125, 39, 23, 127, 48, 75, 104, 78, 54, 53, 177, 125, 33, 29, 42, 49, 173, 205, 5, 9, 225, 99, 169, 16, 177, 95, 186, 134, 218, 129, 179, 15, 219, 90, 103, 54, 188, 25, 90, 235, 144, 137, 45, 35, 66, 53, 45, 183, 232, 27, 20, 50, 91, 219, 194, 187, 55, 146, 113, 126, 233, 186, 161, 15, 163, 116, 104, 114, 2, 161, 118, 1, 163, 116, 120, 110, 140, 163, 102, 101, 101, 206, 0, 3, 200, 192, 162, 102, 118, 206, 0, 14, 249, 218, 163, 103, 101, 110, 172, 100, 101, 118, 110, 101, 116, 45, 118, 51, 56, 46, 48, 162, 103, 104, 196, 32, 254, 179, 108, 57, 16, 20, 57, 0, 195, 218, 85, 66, 202, 24, 54, 176, 15, 210, 248, 25, 89, 18, 87, 205, 35, 246, 4, 47, 152, 200, 54, 157, 162, 108, 118, 206, 0, 14, 253, 194, 166, 115, 101, 108, 107, 101, 121, 196, 32, 50, 18, 43, 43, 214, 61, 220, 83, 49, 150, 23, 165, 170, 83, 196, 177, 194, 111, 227, 220, 202, 242, 141, 54, 34, 181, 105, 119, 161, 64, 92, 134, 163, 115, 110, 100, 196, 32, 141, 146, 180, 137, 144, 1, 115, 160, 77, 250, 67, 89, 163, 102, 106, 106, 252, 234, 44, 66, 160, 93, 217, 193, 247, 62, 235, 165, 71, 128, 55, 233, 164, 116, 121, 112, 101, 166, 107, 101, 121, 114, 101, 103, 167, 118, 111, 116, 101, 102, 115, 116, 206, 0, 13, 187, 160, 166, 118, 111, 116, 101, 107, 100, 205, 39, 16, 167, 118, 111, 116, 101, 107, 101, 121, 196, 32, 112, 27, 215, 251, 145, 43, 7, 179, 8, 17, 255, 40, 29, 159, 238, 149, 99, 229, 128, 46, 32, 38, 137, 35, 25, 37, 143, 119, 250, 147, 30, 136, 167, 118, 111, 116, 101, 108, 115, 116, 206, 0, 15, 66, 64]); + // prettier-ignore + const allThreeBlob = Buffer.from([130, 164, 109, 115, 105, 103, 131, 166, 115, 117, 98, 115, 105, 103, 147, 130, 162, 112, 107, 196, 32, 27, 126, 192, 176, 75, 234, 97, 183, 150, 144, 151, 230, 203, 244, 7, 225, 8, 167, 5, 53, 29, 11, 201, 138, 190, 177, 34, 9, 168, 171, 129, 120, 161, 115, 196, 64, 113, 61, 44, 215, 188, 9, 110, 249, 243, 107, 227, 105, 200, 124, 12, 209, 21, 155, 67, 225, 240, 42, 107, 19, 212, 242, 236, 251, 14, 157, 232, 202, 93, 76, 125, 237, 173, 175, 178, 41, 145, 52, 43, 74, 132, 202, 20, 132, 237, 142, 122, 248, 31, 104, 105, 253, 168, 172, 70, 227, 115, 249, 227, 13, 130, 162, 112, 107, 196, 32, 9, 99, 50, 9, 83, 115, 137, 240, 117, 103, 17, 119, 57, 145, 199, 208, 62, 27, 115, 200, 196, 245, 43, 246, 175, 240, 26, 162, 92, 249, 194, 113, 161, 115, 196, 64, 227, 199, 17, 26, 50, 149, 36, 250, 241, 222, 56, 188, 127, 140, 131, 144, 167, 224, 18, 230, 61, 37, 113, 136, 156, 116, 104, 237, 140, 138, 121, 215, 140, 159, 38, 64, 106, 111, 177, 108, 51, 233, 152, 250, 233, 207, 138, 116, 61, 55, 89, 204, 6, 164, 2, 114, 128, 230, 199, 130, 136, 25, 207, 1, 130, 162, 112, 107, 196, 32, 231, 240, 248, 77, 6, 129, 29, 249, 243, 28, 141, 135, 139, 17, 85, 244, 103, 29, 81, 161, 133, 194, 0, 144, 134, 103, 244, 73, 88, 112, 104, 161, 161, 115, 196, 64, 125, 39, 23, 127, 48, 75, 104, 78, 54, 53, 177, 125, 33, 29, 42, 49, 173, 205, 5, 9, 225, 99, 169, 16, 177, 95, 186, 134, 218, 129, 179, 15, 219, 90, 103, 54, 188, 25, 90, 235, 144, 137, 45, 35, 66, 53, 45, 183, 232, 27, 20, 50, 91, 219, 194, 187, 55, 146, 113, 126, 233, 186, 161, 15, 163, 116, 104, 114, 2, 161, 118, 1, 163, 116, 120, 110, 140, 163, 102, 101, 101, 206, 0, 3, 200, 192, 162, 102, 118, 206, 0, 14, 249, 218, 163, 103, 101, 110, 172, 100, 101, 118, 110, 101, 116, 45, 118, 51, 56, 46, 48, 162, 103, 104, 196, 32, 254, 179, 108, 57, 16, 20, 57, 0, 195, 218, 85, 66, 202, 24, 54, 176, 15, 210, 248, 25, 89, 18, 87, 205, 35, 246, 4, 47, 152, 200, 54, 157, 162, 108, 118, 206, 0, 14, 253, 194, 166, 115, 101, 108, 107, 101, 121, 196, 32, 50, 18, 43, 43, 214, 61, 220, 83, 49, 150, 23, 165, 170, 83, 196, 177, 194, 111, 227, 220, 202, 242, 141, 54, 34, 181, 105, 119, 161, 64, 92, 134, 163, 115, 110, 100, 196, 32, 141, 146, 180, 137, 144, 1, 115, 160, 77, 250, 67, 89, 163, 102, 106, 106, 252, 234, 44, 66, 160, 93, 217, 193, 247, 62, 235, 165, 71, 128, 55, 233, 164, 116, 121, 112, 101, 166, 107, 101, 121, 114, 101, 103, 167, 118, 111, 116, 101, 102, 115, 116, 206, 0, 13, 187, 160, 166, 118, 111, 116, 101, 107, 100, 205, 39, 16, 167, 118, 111, 116, 101, 107, 101, 121, 196, 32, 112, 27, 215, 251, 145, 43, 7, 179, 8, 17, 255, 40, 29, 159, 238, 149, 99, 229, 128, 46, 32, 38, 137, 35, 25, 37, 143, 119, 250, 147, 30, 136, 167, 118, 111, 116, 101, 108, 115, 116, 206, 0, 15, 66, 64]); + + const finMsigBlob = algosdk.mergeMultisigTransactions([ + new Uint8Array(twoAndThreeBlob), + new Uint8Array(oneAndThreeBlob), + ]); + const finMsigBlobTwo = algosdk.mergeMultisigTransactions([ + new Uint8Array(oneAndThreeBlob), + new Uint8Array(twoAndThreeBlob), + ]); + assert.deepStrictEqual(Buffer.from(finMsigBlob), allThreeBlob); + assert.deepStrictEqual(Buffer.from(finMsigBlobTwo), allThreeBlob); + }); + + it('should merge several transactions', () => { + // prettier-ignore + const blobSignedByFirst = Buffer.from([ 130, 164, 109, 115, 105, 103, 131, 166, 115, 117, 98, 115, 105, 103, 147, 130, 162, 112, 107, 196, 32, 27, 126, 192, 176, 75, 234, 97, 183, 150, 144, 151, 230, 203, 244, 7, 225, 8, 167, 5, 53, 29, 11, 201, 138, 190, 177, 34, 9, 168, 171, 129, 120, 161, 115, 196, 64, 113, 61, 44, 215, 188, 9, 110, 249, 243, 107, 227, 105, 200, 124, 12, 209, 21, 155, 67, 225, 240, 42, 107, 19, 212, 242, 236, 251, 14, 157, 232, 202, 93, 76, 125, 237, 173, 175, 178, 41, 145, 52, 43, 74, 132, 202, 20, 132, 237, 142, 122, 248, 31, 104, 105, 253, 168, 172, 70, 227, 115, 249, 227, 13, 129, 162, 112, 107, 196, 32, 9, 99, 50, 9, 83, 115, 137, 240, 117, 103, 17, 119, 57, 145, 199, 208, 62, 27, 115, 200, 196, 245, 43, 246, 175, 240, 26, 162, 92, 249, 194, 113, 129, 162, 112, 107, 196, 32, 231, 240, 248, 77, 6, 129, 29, 249, 243, 28, 141, 135, 139, 17, 85, 244, 103, 29, 81, 161, 133, 194, 0, 144, 134, 103, 244, 73, 88, 112, 104, 161, 163, 116, 104, 114, 2, 161, 118, 1, 163, 116, 120, 110, 140, 163, 102, 101, 101, 206, 0, 3, 200, 192, 162, 102, 118, 206, 0, 14, 249, 218, 163, 103, 101, 110, 172, 100, 101, 118, 110, 101, 116, 45, 118, 51, 56, 46, 48, 162, 103, 104, 196, 32, 254, 179, 108, 57, 16, 20, 57, 0, 195, 218, 85, 66, 202, 24, 54, 176, 15, 210, 248, 25, 89, 18, 87, 205, 35, 246, 4, 47, 152, 200, 54, 157, 162, 108, 118, 206, 0, 14, 253, 194, 166, 115, 101, 108, 107, 101, 121, 196, 32, 50, 18, 43, 43, 214, 61, 220, 83, 49, 150, 23, 165, 170, 83, 196, 177, 194, 111, 227, 220, 202, 242, 141, 54, 34, 181, 105, 119, 161, 64, 92, 134, 163, 115, 110, 100, 196, 32, 141, 146, 180, 137, 144, 1, 115, 160, 77, 250, 67, 89, 163, 102, 106, 106, 252, 234, 44, 66, 160, 93, 217, 193, 247, 62, 235, 165, 71, 128, 55, 233, 164, 116, 121, 112, 101, 166, 107, 101, 121, 114, 101, 103, 167, 118, 111, 116, 101, 102, 115, 116, 206, 0, 13, 187, 160, 166, 118, 111, 116, 101, 107, 100, 205, 39, 16, 167, 118, 111, 116, 101, 107, 101, 121, 196, 32, 112, 27, 215, 251, 145, 43, 7, 179, 8, 17, 255, 40, 29, 159, 238, 149, 99, 229, 128, 46, 32, 38, 137, 35, 25, 37, 143, 119, 250, 147, 30, 136, 167, 118, 111, 116, 101, 108, 115, 116, 206, 0, 15, 66, 64, ]); + // prettier-ignore + const blobSignedBySecond = Buffer.from([ 130, 164, 109, 115, 105, 103, 131, 166, 115, 117, 98, 115, 105, 103, 147, 129, 162, 112, 107, 196, 32, 27, 126, 192, 176, 75, 234, 97, 183, 150, 144, 151, 230, 203, 244, 7, 225, 8, 167, 5, 53, 29, 11, 201, 138, 190, 177, 34, 9, 168, 171, 129, 120, 130, 162, 112, 107, 196, 32, 9, 99, 50, 9, 83, 115, 137, 240, 117, 103, 17, 119, 57, 145, 199, 208, 62, 27, 115, 200, 196, 245, 43, 246, 175, 240, 26, 162, 92, 249, 194, 113, 161, 115, 196, 64, 227, 199, 17, 26, 50, 149, 36, 250, 241, 222, 56, 188, 127, 140, 131, 144, 167, 224, 18, 230, 61, 37, 113, 136, 156, 116, 104, 237, 140, 138, 121, 215, 140, 159, 38, 64, 106, 111, 177, 108, 51, 233, 152, 250, 233, 207, 138, 116, 61, 55, 89, 204, 6, 164, 2, 114, 128, 230, 199, 130, 136, 25, 207, 1, 129, 162, 112, 107, 196, 32, 231, 240, 248, 77, 6, 129, 29, 249, 243, 28, 141, 135, 139, 17, 85, 244, 103, 29, 81, 161, 133, 194, 0, 144, 134, 103, 244, 73, 88, 112, 104, 161, 163, 116, 104, 114, 2, 161, 118, 1, 163, 116, 120, 110, 140, 163, 102, 101, 101, 206, 0, 3, 200, 192, 162, 102, 118, 206, 0, 14, 249, 218, 163, 103, 101, 110, 172, 100, 101, 118, 110, 101, 116, 45, 118, 51, 56, 46, 48, 162, 103, 104, 196, 32, 254, 179, 108, 57, 16, 20, 57, 0, 195, 218, 85, 66, 202, 24, 54, 176, 15, 210, 248, 25, 89, 18, 87, 205, 35, 246, 4, 47, 152, 200, 54, 157, 162, 108, 118, 206, 0, 14, 253, 194, 166, 115, 101, 108, 107, 101, 121, 196, 32, 50, 18, 43, 43, 214, 61, 220, 83, 49, 150, 23, 165, 170, 83, 196, 177, 194, 111, 227, 220, 202, 242, 141, 54, 34, 181, 105, 119, 161, 64, 92, 134, 163, 115, 110, 100, 196, 32, 141, 146, 180, 137, 144, 1, 115, 160, 77, 250, 67, 89, 163, 102, 106, 106, 252, 234, 44, 66, 160, 93, 217, 193, 247, 62, 235, 165, 71, 128, 55, 233, 164, 116, 121, 112, 101, 166, 107, 101, 121, 114, 101, 103, 167, 118, 111, 116, 101, 102, 115, 116, 206, 0, 13, 187, 160, 166, 118, 111, 116, 101, 107, 100, 205, 39, 16, 167, 118, 111, 116, 101, 107, 101, 121, 196, 32, 112, 27, 215, 251, 145, 43, 7, 179, 8, 17, 255, 40, 29, 159, 238, 149, 99, 229, 128, 46, 32, 38, 137, 35, 25, 37, 143, 119, 250, 147, 30, 136, 167, 118, 111, 116, 101, 108, 115, 116, 206, 0, 15, 66, 64, ]); + // prettier-ignore + const blobSignedByThird = Buffer.from([ 130, 164, 109, 115, 105, 103, 131, 166, 115, 117, 98, 115, 105, 103, 147, 129, 162, 112, 107, 196, 32, 27, 126, 192, 176, 75, 234, 97, 183, 150, 144, 151, 230, 203, 244, 7, 225, 8, 167, 5, 53, 29, 11, 201, 138, 190, 177, 34, 9, 168, 171, 129, 120, 129, 162, 112, 107, 196, 32, 9, 99, 50, 9, 83, 115, 137, 240, 117, 103, 17, 119, 57, 145, 199, 208, 62, 27, 115, 200, 196, 245, 43, 246, 175, 240, 26, 162, 92, 249, 194, 113, 130, 162, 112, 107, 196, 32, 231, 240, 248, 77, 6, 129, 29, 249, 243, 28, 141, 135, 139, 17, 85, 244, 103, 29, 81, 161, 133, 194, 0, 144, 134, 103, 244, 73, 88, 112, 104, 161, 161, 115, 196, 64, 125, 39, 23, 127, 48, 75, 104, 78, 54, 53, 177, 125, 33, 29, 42, 49, 173, 205, 5, 9, 225, 99, 169, 16, 177, 95, 186, 134, 218, 129, 179, 15, 219, 90, 103, 54, 188, 25, 90, 235, 144, 137, 45, 35, 66, 53, 45, 183, 232, 27, 20, 50, 91, 219, 194, 187, 55, 146, 113, 126, 233, 186, 161, 15, 163, 116, 104, 114, 2, 161, 118, 1, 163, 116, 120, 110, 140, 163, 102, 101, 101, 206, 0, 3, 200, 192, 162, 102, 118, 206, 0, 14, 249, 218, 163, 103, 101, 110, 172, 100, 101, 118, 110, 101, 116, 45, 118, 51, 56, 46, 48, 162, 103, 104, 196, 32, 254, 179, 108, 57, 16, 20, 57, 0, 195, 218, 85, 66, 202, 24, 54, 176, 15, 210, 248, 25, 89, 18, 87, 205, 35, 246, 4, 47, 152, 200, 54, 157, 162, 108, 118, 206, 0, 14, 253, 194, 166, 115, 101, 108, 107, 101, 121, 196, 32, 50, 18, 43, 43, 214, 61, 220, 83, 49, 150, 23, 165, 170, 83, 196, 177, 194, 111, 227, 220, 202, 242, 141, 54, 34, 181, 105, 119, 161, 64, 92, 134, 163, 115, 110, 100, 196, 32, 141, 146, 180, 137, 144, 1, 115, 160, 77, 250, 67, 89, 163, 102, 106, 106, 252, 234, 44, 66, 160, 93, 217, 193, 247, 62, 235, 165, 71, 128, 55, 233, 164, 116, 121, 112, 101, 166, 107, 101, 121, 114, 101, 103, 167, 118, 111, 116, 101, 102, 115, 116, 206, 0, 13, 187, 160, 166, 118, 111, 116, 101, 107, 100, 205, 39, 16, 167, 118, 111, 116, 101, 107, 101, 121, 196, 32, 112, 27, 215, 251, 145, 43, 7, 179, 8, 17, 255, 40, 29, 159, 238, 149, 99, 229, 128, 46, 32, 38, 137, 35, 25, 37, 143, 119, 250, 147, 30, 136, 167, 118, 111, 116, 101, 108, 115, 116, 206, 0, 15, 66, 64, ]); + // prettier-ignore + const blobSignedByAllThree = Buffer.from([130, 164, 109, 115, 105, 103, 131, 166, 115, 117, 98, 115, 105, 103, 147, 130, 162, 112, 107, 196, 32, 27, 126, 192, 176, 75, 234, 97, 183, 150, 144, 151, 230, 203, 244, 7, 225, 8, 167, 5, 53, 29, 11, 201, 138, 190, 177, 34, 9, 168, 171, 129, 120, 161, 115, 196, 64, 113, 61, 44, 215, 188, 9, 110, 249, 243, 107, 227, 105, 200, 124, 12, 209, 21, 155, 67, 225, 240, 42, 107, 19, 212, 242, 236, 251, 14, 157, 232, 202, 93, 76, 125, 237, 173, 175, 178, 41, 145, 52, 43, 74, 132, 202, 20, 132, 237, 142, 122, 248, 31, 104, 105, 253, 168, 172, 70, 227, 115, 249, 227, 13, 130, 162, 112, 107, 196, 32, 9, 99, 50, 9, 83, 115, 137, 240, 117, 103, 17, 119, 57, 145, 199, 208, 62, 27, 115, 200, 196, 245, 43, 246, 175, 240, 26, 162, 92, 249, 194, 113, 161, 115, 196, 64, 227, 199, 17, 26, 50, 149, 36, 250, 241, 222, 56, 188, 127, 140, 131, 144, 167, 224, 18, 230, 61, 37, 113, 136, 156, 116, 104, 237, 140, 138, 121, 215, 140, 159, 38, 64, 106, 111, 177, 108, 51, 233, 152, 250, 233, 207, 138, 116, 61, 55, 89, 204, 6, 164, 2, 114, 128, 230, 199, 130, 136, 25, 207, 1, 130, 162, 112, 107, 196, 32, 231, 240, 248, 77, 6, 129, 29, 249, 243, 28, 141, 135, 139, 17, 85, 244, 103, 29, 81, 161, 133, 194, 0, 144, 134, 103, 244, 73, 88, 112, 104, 161, 161, 115, 196, 64, 125, 39, 23, 127, 48, 75, 104, 78, 54, 53, 177, 125, 33, 29, 42, 49, 173, 205, 5, 9, 225, 99, 169, 16, 177, 95, 186, 134, 218, 129, 179, 15, 219, 90, 103, 54, 188, 25, 90, 235, 144, 137, 45, 35, 66, 53, 45, 183, 232, 27, 20, 50, 91, 219, 194, 187, 55, 146, 113, 126, 233, 186, 161, 15, 163, 116, 104, 114, 2, 161, 118, 1, 163, 116, 120, 110, 140, 163, 102, 101, 101, 206, 0, 3, 200, 192, 162, 102, 118, 206, 0, 14, 249, 218, 163, 103, 101, 110, 172, 100, 101, 118, 110, 101, 116, 45, 118, 51, 56, 46, 48, 162, 103, 104, 196, 32, 254, 179, 108, 57, 16, 20, 57, 0, 195, 218, 85, 66, 202, 24, 54, 176, 15, 210, 248, 25, 89, 18, 87, 205, 35, 246, 4, 47, 152, 200, 54, 157, 162, 108, 118, 206, 0, 14, 253, 194, 166, 115, 101, 108, 107, 101, 121, 196, 32, 50, 18, 43, 43, 214, 61, 220, 83, 49, 150, 23, 165, 170, 83, 196, 177, 194, 111, 227, 220, 202, 242, 141, 54, 34, 181, 105, 119, 161, 64, 92, 134, 163, 115, 110, 100, 196, 32, 141, 146, 180, 137, 144, 1, 115, 160, 77, 250, 67, 89, 163, 102, 106, 106, 252, 234, 44, 66, 160, 93, 217, 193, 247, 62, 235, 165, 71, 128, 55, 233, 164, 116, 121, 112, 101, 166, 107, 101, 121, 114, 101, 103, 167, 118, 111, 116, 101, 102, 115, 116, 206, 0, 13, 187, 160, 166, 118, 111, 116, 101, 107, 100, 205, 39, 16, 167, 118, 111, 116, 101, 107, 101, 121, 196, 32, 112, 27, 215, 251, 145, 43, 7, 179, 8, 17, 255, 40, 29, 159, 238, 149, 99, 229, 128, 46, 32, 38, 137, 35, 25, 37, 143, 119, 250, 147, 30, 136, 167, 118, 111, 116, 101, 108, 115, 116, 206, 0, 15, 66, 64]); + + const finMsigBlob = algosdk.mergeMultisigTransactions([ + new Uint8Array(blobSignedByFirst), + new Uint8Array(blobSignedBySecond), + new Uint8Array(blobSignedByThird), + ]); + const finMsigBlobTwo = algosdk.mergeMultisigTransactions([ + new Uint8Array(blobSignedByThird), + new Uint8Array(blobSignedByFirst), + new Uint8Array(blobSignedBySecond), + ]); + + // let's open those to verify all sigs are there + let { msig } = algosdk.decodeSignedTransaction(finMsigBlob); + assert( + msig && msig.subsig.every((sig) => ArrayBuffer.isView(sig.s)), + 'missing signatures' + ); + + ({ msig } = algosdk.decodeSignedTransaction(finMsigBlobTwo)); + assert( + msig && msig.subsig.every((sig) => ArrayBuffer.isView(sig.s)), + 'missing signatures' + ); + + // let's check the merged transactions against our reference blob + assert.deepStrictEqual(Buffer.from(finMsigBlob), blobSignedByAllThree); + assert.deepStrictEqual(Buffer.from(finMsigBlobTwo), blobSignedByAllThree); + }); + + it('should correctly handle a different sender', () => { + const oneAndThreeBlob = Buffer.from( + 'g6Rtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAWLcyn6nB68yo/PUHXB21wyTy+PhHgMQNOIXPTGB96faZP2xqpQ8IFlIR2LPotlX68ylK8MCl82SUfMR4FYyzAIGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgqJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGhc8RAQ3t8lAipybLbWaRa3EDKl0mLGwUpYzl0iBCHSflhJM6zboJiT10FnWtXqW0dUBzpj9yeqtSuSJyKb8Ml3YJkCqN0aHICoXYBpHNnbnLEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfpo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNA+iiZnbN8xWjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbN9v2kbm90ZcQIRSYiABhShvujcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEICHMyArhQUJN+4MirQrVopinURoL2GOodR8RZxvZFZl5pHR5cGWjcGF5', + 'base64' + ); + const twoAndThreeBlob = Buffer.from( + 'g6Rtc2lng6ZzdWJzaWeTgaJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXiConBrxCAJYzIJU3OJ8HVnEXc5kcfQPhtzyMT1K/av8BqiXPnCcaFzxED7h9lpTNsdbX5QOixjISlwYrju3bEc412D4HH47w7YfWbAms8jih6kJrWIw/qVyzPHyW60YaFBk5Mi8SDYk/cAgqJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGhc8RAQ3t8lAipybLbWaRa3EDKl0mLGwUpYzl0iBCHSflhJM6zboJiT10FnWtXqW0dUBzpj9yeqtSuSJyKb8Ml3YJkCqN0aHICoXYBpHNnbnLEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfpo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNA+iiZnbN8xWjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbN9v2kbm90ZcQIRSYiABhShvujcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEICHMyArhQUJN+4MirQrVopinURoL2GOodR8RZxvZFZl5pHR5cGWjcGF5', + 'base64' + ); + const allThreeBlob = Buffer.from( + 'g6Rtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAWLcyn6nB68yo/PUHXB21wyTy+PhHgMQNOIXPTGB96faZP2xqpQ8IFlIR2LPotlX68ylK8MCl82SUfMR4FYyzAIKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQPuH2WlM2x1tflA6LGMhKXBiuO7dsRzjXYPgcfjvDth9ZsCazyOKHqQmtYjD+pXLM8fJbrRhoUGTkyLxINiT9wCConBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaFzxEBDe3yUCKnJsttZpFrcQMqXSYsbBSljOXSIEIdJ+WEkzrNugmJPXQWda1epbR1QHOmP3J6q1K5InIpvwyXdgmQKo3RocgKhdgGkc2ducsQgjZK0iZABc6BN+kNZo2ZqavzqLEKgXdnB9z7rpUeAN+mjdHhui6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0D6KJmds3zFaNnZW6sZGV2bmV0LXYzOC4womdoxCD+s2w5EBQ5AMPaVULKGDawD9L4GVkSV80j9gQvmMg2naJsds32/aRub3RlxAhFJiIAGFKG+6NyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQgIczICuFBQk37gyKtCtWimKdRGgvYY6h1HxFnG9kVmXmkdHlwZaNwYXk=', + 'base64' + ); + + const finMsigBlob = algosdk.mergeMultisigTransactions([ + new Uint8Array(twoAndThreeBlob), + new Uint8Array(oneAndThreeBlob), + ]); + const finMsigBlobTwo = algosdk.mergeMultisigTransactions([ + new Uint8Array(oneAndThreeBlob), + new Uint8Array(twoAndThreeBlob), + ]); + assert.deepStrictEqual(Buffer.from(finMsigBlob), allThreeBlob); + assert.deepStrictEqual(Buffer.from(finMsigBlobTwo), allThreeBlob); + }); + }); + + describe('read-only transaction methods should work as expected on multisig transactions', () => { + let stdPaymentTxn; + let msigPaymentTxn; + + let stdKeyregTxn; + let msigKeyregTxn; + + // Create a multisig transaction to use for each test + beforeEach(() => { + const paymentTxnObj = { + snd: Buffer.from( + algosdk.decodeAddress( + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + ).publicKey + ), + rcv: Buffer.from( + algosdk.decodeAddress( + 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI' + ).publicKey + ), + fee: 1000, + amt: 1000, + close: Buffer.from( + algosdk.decodeAddress( + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA' + ).publicKey + ), + gh: Buffer.from( + '/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp0=', + 'base64' + ), + fv: 62229, + lv: 63229, + gen: 'devnet-v38.0', + type: 'pay', + note: Buffer.from('RSYiABhShvs=', 'base64'), + }; + + stdPaymentTxn = algosdk.Transaction.from_obj_for_encoding(paymentTxnObj); + msigPaymentTxn = MultisigTransaction.from_obj_for_encoding(paymentTxnObj); + + const keyregTxnObj = { + snd: Buffer.from( + algosdk.decodeAddress( + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + ).publicKey + ), + fee: 10, + fv: 51, + lv: 61, + note: Buffer.from([123, 12, 200]), + gh: Buffer.from( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + 'base64' + ), + votekey: Buffer.from( + '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE=', + 'base64' + ), + selkey: Buffer.from( + 'oImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX4=', + 'base64' + ), + votefst: 123, + votelst: 456, + votekd: 1234, + gen: 'devnet-v38.0', + type: 'keyreg', + }; + + stdKeyregTxn = algosdk.Transaction.from_obj_for_encoding(keyregTxnObj); + msigKeyregTxn = MultisigTransaction.from_obj_for_encoding(keyregTxnObj); + }); + + it('`estimateSize` method should match expected result', () => { + assert.strictEqual( + stdPaymentTxn.estimateSize(), + msigPaymentTxn.estimateSize() + ); + assert.strictEqual( + stdKeyregTxn.estimateSize(), + msigKeyregTxn.estimateSize() + ); + }); + + it('`txID` method should match expected result', () => { + assert.strictEqual(stdPaymentTxn.txID(), msigPaymentTxn.txID()); + assert.strictEqual(stdKeyregTxn.txID(), msigKeyregTxn.txID()); + }); + + it('`toString` method should match expected result', () => { + assert.strictEqual(stdPaymentTxn.toString(), msigPaymentTxn.toString()); + assert.strictEqual(stdKeyregTxn.toString(), msigKeyregTxn.toString()); + }); + }); + + describe('inherited MultisigTransaction methods that mutate transactions should throw errors', () => { + let msigPaymentTxn; + let msigKeyregTxn; + + // Create a multisig transaction to use for each test + beforeEach(() => { + const paymentTxnObj = { + snd: Buffer.from( + algosdk.decodeAddress( + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + ).publicKey + ), + rcv: Buffer.from( + algosdk.decodeAddress( + 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI' + ).publicKey + ), + fee: 1000, + amt: 1000, + close: Buffer.from( + algosdk.decodeAddress( + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA' + ).publicKey + ), + gh: Buffer.from( + '/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp0=', + 'base64' + ), + fv: 62229, + lv: 63229, + gen: 'devnet-v38.0', + type: 'pay', + note: Buffer.from('RSYiABhShvs=', 'base64'), + }; + + const keyregTxnObj = { + snd: Buffer.from( + algosdk.decodeAddress( + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + ).publicKey + ), + fee: 10, + fv: 51, + lv: 61, + note: Buffer.from([123, 12, 200]), + gh: Buffer.from( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + 'base64' + ), + votekey: Buffer.from( + '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE=', + 'base64' + ), + selkey: Buffer.from( + 'oImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX4=', + 'base64' + ), + votefst: 123, + votelst: 456, + votekd: 1234, + gen: 'devnet-v38.0', + type: 'keyreg', + }; + + msigPaymentTxn = MultisigTransaction.from_obj_for_encoding(paymentTxnObj); + msigKeyregTxn = MultisigTransaction.from_obj_for_encoding(keyregTxnObj); + }); + + it('error should be thrown when attempting to add a lease to a transaction', () => { + assert.throws( + msigPaymentTxn.addLease, + (err) => err.message === MULTISIG_NO_MUTATE_ERROR_MSG + ); + assert.throws( + msigKeyregTxn.addLease, + (err) => err.message === MULTISIG_NO_MUTATE_ERROR_MSG + ); + }); + + it('error should be thrown when attempting to add a rekey to a transaction', () => { + assert.throws( + msigPaymentTxn.addRekey, + (err) => err.message === MULTISIG_NO_MUTATE_ERROR_MSG + ); + assert.throws( + msigKeyregTxn.addRekey, + (err) => err.message === MULTISIG_NO_MUTATE_ERROR_MSG + ); + }); + }); + + describe('error should be thrown when attempting to sign a transaction', () => { + let msigPaymentTxn; + let msigKeyregTxn; + + // Create a multisig transaction to use for each test + beforeEach(() => { + const paymentTxnObj = { + snd: Buffer.from( + algosdk.decodeAddress( + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + ).publicKey + ), + rcv: Buffer.from( + algosdk.decodeAddress( + 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI' + ).publicKey + ), + fee: 1000, + amt: 1000, + close: Buffer.from( + algosdk.decodeAddress( + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA' + ).publicKey + ), + gh: Buffer.from( + '/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp0=', + 'base64' + ), + fv: 62229, + lv: 63229, + gen: 'devnet-v38.0', + type: 'pay', + note: Buffer.from('RSYiABhShvs=', 'base64'), + }; + + const keyregTxnObj = { + snd: Buffer.from( + algosdk.decodeAddress( + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + ).publicKey + ), + fee: 10, + fv: 51, + lv: 61, + note: Buffer.from([123, 12, 200]), + gh: Buffer.from( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + 'base64' + ), + votekey: Buffer.from( + '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE=', + 'base64' + ), + selkey: Buffer.from( + 'oImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX4=', + 'base64' + ), + votefst: 123, + votelst: 456, + votekd: 1234, + gen: 'devnet-v38.0', + type: 'keyreg', + }; + + msigPaymentTxn = MultisigTransaction.from_obj_for_encoding(paymentTxnObj); + msigKeyregTxn = MultisigTransaction.from_obj_for_encoding(keyregTxnObj); + }); + + it('signTxn method should throw an error', () => { + assert.throws( + msigPaymentTxn.signTxn, + (err) => err.message === MULTISIG_USE_PARTIAL_SIGN_ERROR_MSG + ); + assert.throws( + msigKeyregTxn.signTxn, + (err) => err.message === MULTISIG_USE_PARTIAL_SIGN_ERROR_MSG + ); + }); + }); +}); diff --git a/tests/7.AlgoSDK.js b/tests/7.AlgoSDK.js new file mode 100644 index 0000000..e54a597 --- /dev/null +++ b/tests/7.AlgoSDK.js @@ -0,0 +1,1078 @@ +/* eslint-env mocha */ +const { Buffer } = require('buffer'); +const assert = require('assert'); +const algosdk = require('../src/index'); +const nacl = require('../src/nacl/naclWrappers'); +const utils = require('../src/utils/utils'); + +describe('Algosdk (AKA end to end)', () => { + describe('#mnemonic', () => { + it('should export and import', () => { + for (let i = 0; i < 50; i++) { + const keys = algosdk.generateAccount(); + const mn = algosdk.secretKeyToMnemonic(keys.sk); + const recovered = algosdk.mnemonicToSecretKey(mn); + assert.deepStrictEqual(keys.sk, recovered.sk); + assert.deepStrictEqual(keys.addr, recovered.addr); + } + }); + }); + + describe('#encoding', () => { + it('should encode and decode', () => { + const o = { a: [1, 2, 3, 4, 5], b: 3486, c: 'skfg' }; + assert.deepStrictEqual(o, algosdk.decodeObj(algosdk.encodeObj(o))); + }); + + it('should encode and decode strings', () => { + const o = 'Hi there'; + assert.deepStrictEqual(o, algosdk.decodeObj(algosdk.encodeObj(o))); + }); + + it('should not mutate unsigned transaction when going to or from encoded buffer', () => { + const to = 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI'; + const fee = 4; + const amount = 1000; + const firstRound = 12466; + const lastRound = 13466; + const genesisID = 'devnet-v33.0'; + const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const closeRemainderTo = + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA'; + const note = new Uint8Array(Buffer.from('6gAVR0Nsv5Y=', 'base64')); + const from = to; + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const txnAsObj = algosdk.makePaymentTxnWithSuggestedParams( + from, + to, + amount, + closeRemainderTo, + note, + suggestedParams + ); + const txnAsBuffer = algosdk.encodeUnsignedTransaction(txnAsObj); + const txnAsObjRecovered = algosdk.decodeUnsignedTransaction(txnAsBuffer); + const txnAsBufferRecovered = algosdk.encodeUnsignedTransaction( + txnAsObjRecovered + ); + assert.deepStrictEqual(txnAsBuffer, txnAsBufferRecovered); + const txnAsBufferGolden = new Uint8Array( + Buffer.from( + 'i6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0EmKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqRub3RlxAjqABVHQ2y/lqNyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKkdHlwZaNwYXk=', + 'base64' + ) + ); + assert.deepStrictEqual(txnAsBufferGolden, txnAsBufferRecovered); + }); + + it('should not mutate signed transaction when going to or from encoded buffer', () => { + const to = 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI'; + const fee = 4; + const amount = 1000; + const firstRound = 12466; + const lastRound = 13466; + const genesisID = 'devnet-v33.0'; + const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const closeRemainderTo = + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA'; + const note = new Uint8Array(Buffer.from('6gAVR0Nsv5Y=', 'base64')); + const from = to; + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const txnAsObj = algosdk.makePaymentTxnWithSuggestedParams( + from, + to, + amount, + closeRemainderTo, + note, + suggestedParams + ); + let sk = + 'advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor'; + sk = algosdk.mnemonicToSecretKey(sk); + const initialSignedTxnBytes = txnAsObj.signTxn(sk.sk); + const signedTxnRecovered = algosdk.decodeSignedTransaction( + initialSignedTxnBytes + ); + const txnAsObjRecovered = signedTxnRecovered.txn; + const recoveredSignedTxnBytes = txnAsObjRecovered.signTxn(sk.sk); + assert.deepStrictEqual(initialSignedTxnBytes, recoveredSignedTxnBytes); + const signedTxnBytesGolden = new Uint8Array( + Buffer.from( + 'g6RzZ25yxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaNzaWfEQDJHtrytU9p3nhRH1XS8tX+KmeKGyekigG7M704dOkBMTqiOJFuukbK2gUViJtivsPrKNiV0+WIrdbBk7gmNkgGjdHhui6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0EmKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqRub3RlxAjqABVHQ2y/lqNyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKkdHlwZaNwYXk=', + 'base64' + ) + ); + assert.deepStrictEqual(signedTxnBytesGolden, recoveredSignedTxnBytes); + }); + }); + + describe('Sign', () => { + it('should return a blob that matches the go code', () => { + let sk = + 'advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor'; + const golden = + 'gqNzaWfEQPhUAZ3xkDDcc8FvOVo6UinzmKBCqs0woYSfodlmBMfQvGbeUx3Srxy3dyJDzv7rLm26BRv9FnL2/AuT7NYfiAWjdHhui6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0EmKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqRub3RlxAjqABVHQ2y/lqNyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGkdHlwZaNwYXk='; + const o = { + to: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', + fee: 4, + amount: 1000, + firstRound: 12466, + lastRound: 13466, + genesisID: 'devnet-v33.0', + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + closeRemainderTo: + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA', + note: new Uint8Array(Buffer.from('6gAVR0Nsv5Y=', 'base64')), + }; + + sk = algosdk.mnemonicToSecretKey(sk); + + const jsDec = algosdk.signTransaction(o, sk.sk); + assert.deepStrictEqual( + Buffer.from(jsDec.blob), + Buffer.from(golden, 'base64') + ); + + // // Check txid + const txGolden = '5FJDJD5LMZC3EHUYYJNH5I23U4X6H2KXABNDGPIL557ZMJ33GZHQ'; + assert.deepStrictEqual(jsDec.txID, txGolden); + }); + + it('should return a blob that matches the go code when using a flat fee', () => { + let sk = + 'advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor'; + const golden = + 'gqNzaWfEQPhUAZ3xkDDcc8FvOVo6UinzmKBCqs0woYSfodlmBMfQvGbeUx3Srxy3dyJDzv7rLm26BRv9FnL2/AuT7NYfiAWjdHhui6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0EmKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqRub3RlxAjqABVHQ2y/lqNyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGkdHlwZaNwYXk='; + const o = { + to: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', + fee: 1176, + amount: 1000, + firstRound: 12466, + lastRound: 13466, + genesisID: 'devnet-v33.0', + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + closeRemainderTo: + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA', + note: new Uint8Array(Buffer.from('6gAVR0Nsv5Y=', 'base64')), + flatFee: true, + }; + + sk = algosdk.mnemonicToSecretKey(sk); + + const jsDec = algosdk.signTransaction(o, sk.sk); + assert.deepStrictEqual( + Buffer.from(jsDec.blob), + Buffer.from(golden, 'base64') + ); + + // // Check txid + const txGolden = '5FJDJD5LMZC3EHUYYJNH5I23U4X6H2KXABNDGPIL557ZMJ33GZHQ'; + assert.deepStrictEqual(jsDec.txID, txGolden); + }); + + it('should return a blob that matches the go code when constructing with a lease', () => { + let sk = + 'advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor'; + const golden = + 'gqNzaWfEQOMmFSIKsZvpW0txwzhmbgQjxv6IyN7BbV5sZ2aNgFbVcrWUnqPpQQxfPhV/wdu9jzEPUU1jAujYtcNCxJ7ONgejdHhujKNhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0FLKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqJseMQgAQIDBAECAwQBAgMEAQIDBAECAwQBAgMEAQIDBAECAwSkbm90ZcQI6gAVR0Nsv5ajcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEIOfw+E0GgR358xyNh4sRVfRnHVGhhcIAkIZn9ElYcGihpHR5cGWjcGF5'; + // prettier-ignore + const lease = new Uint8Array([1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]); + const o = { + to: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', + fee: 4, + amount: 1000, + firstRound: 12466, + lastRound: 13466, + genesisID: 'devnet-v33.0', + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + closeRemainderTo: + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA', + note: new Uint8Array(Buffer.from('6gAVR0Nsv5Y=', 'base64')), + lease, + }; + + sk = algosdk.mnemonicToSecretKey(sk); + + const jsDec = algosdk.signTransaction(o, sk.sk); + assert.deepStrictEqual( + Buffer.from(jsDec.blob), + Buffer.from(golden, 'base64') + ); + + // Check txid + const txGolden = '7BG6COBZKF6I6W5XY72ZE4HXV6LLZ6ENSR6DASEGSTXYXR4XJOOQ'; + assert.deepStrictEqual(jsDec.txID, txGolden); + }); + + it('should return a blob that matches the go code when adding a lease', () => { + let sk = + 'advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor'; + const golden = + 'gqNzaWfEQOMmFSIKsZvpW0txwzhmbgQjxv6IyN7BbV5sZ2aNgFbVcrWUnqPpQQxfPhV/wdu9jzEPUU1jAujYtcNCxJ7ONgejdHhujKNhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0FLKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqJseMQgAQIDBAECAwQBAgMEAQIDBAECAwQBAgMEAQIDBAECAwSkbm90ZcQI6gAVR0Nsv5ajcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEIOfw+E0GgR358xyNh4sRVfRnHVGhhcIAkIZn9ElYcGihpHR5cGWjcGF5'; + // prettier-ignore + const lease = new Uint8Array([1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]); + const to = 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI'; + const fee = 4; + const amount = 1000; + const firstRound = 12466; + const lastRound = 13466; + const genesisID = 'devnet-v33.0'; + const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const closeRemainderTo = + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA'; + const note = new Uint8Array(Buffer.from('6gAVR0Nsv5Y=', 'base64')); + sk = algosdk.mnemonicToSecretKey(sk); + const key = nacl.keyPairFromSecretKey(sk.sk); + const from = algosdk.encodeAddress(key.publicKey); + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const txn = algosdk.makePaymentTxnWithSuggestedParams( + from, + to, + amount, + closeRemainderTo, + note, + suggestedParams + ); + txn.addLease(lease, fee); + + const txnBytes = txn.signTxn(sk.sk); + assert.deepStrictEqual( + Buffer.from(txnBytes), + Buffer.from(golden, 'base64') + ); + + // Check txid + const txGolden = '7BG6COBZKF6I6W5XY72ZE4HXV6LLZ6ENSR6DASEGSTXYXR4XJOOQ'; + assert.deepStrictEqual(txn.txID().toString(), txGolden); + }); + }); + + describe('Sign and verify bytes', () => { + it('should verify a correct signature', () => { + const account = algosdk.generateAccount(); + const toSign = new Uint8Array(Buffer.from([1, 9, 25, 49])); + const signed = algosdk.signBytes(toSign, account.sk); + assert.equal(true, algosdk.verifyBytes(toSign, signed, account.addr)); + }); + + it('should not verify a corrupted signature', () => { + const account = algosdk.generateAccount(); + const toSign = Buffer.from([1, 9, 25, 49]); + const signed = algosdk.signBytes(toSign, account.sk); + signed[0] = (signed[0] + 1) % 256; + assert.equal(false, algosdk.verifyBytes(toSign, signed, account.addr)); + }); + + it('should attach arbitrary signatures', () => { + const sender = algosdk.generateAccount(); + const signer = algosdk.generateAccount(); + + // Create a transaction + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: sender.addr, + to: signer.addr, + amount: 1000, + suggestedParams: { + firstRound: 12466, + lastRound: 13466, + genesisID: 'devnet-v33.0', + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + fee: 4, + }, + }); + + // Sign it directly to get a signature + const signedWithSk = txn.signTxn(signer.sk); + const decoded = algosdk.decodeObj(signedWithSk); + const signature = decoded.sig; + + // Attach the signature to the transaction indirectly, and compare + const signedWithSignature = txn.attachSignature(signer.addr, signature); + assert.deepEqual(signedWithSk, signedWithSignature); + + // Check that signer was set + const decodedWithSigner = algosdk.decodeObj(signedWithSignature); + assert.deepEqual( + decodedWithSigner.sgnr, + algosdk.decodeAddress(signer.addr).publicKey + ); + }); + + it('should not attach signature with incorrect length', () => { + const sender = algosdk.generateAccount(); + const signer = algosdk.generateAccount(); + + // Create a transaction + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: sender.addr, + to: signer.addr, + amount: 1000, + suggestedParams: { + firstRound: 12466, + lastRound: 13466, + genesisID: 'devnet-v33.0', + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + fee: 4, + }, + }); + + // Sign it directly to get a signature + const signedWithSk = txn.signTxn(signer.sk); + const decoded = algosdk.decodeObj(signedWithSk); + const signature = decoded.sig.slice(0, -1); // without the last byte + + // Check that the signature is not attached + assert.throws( + () => txn.attachSignature(signer.addr, signature), + Error('Invalid signature length') + ); + }); + }); + + describe('Multisig Sign', () => { + it('should return a blob that matches the go code', () => { + const params = { + version: 1, + threshold: 2, + addrs: [ + 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA', + 'BFRTECKTOOE7A5LHCF3TTEOH2A7BW46IYT2SX5VP6ANKEXHZYJY77SJTVM', + '47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU', + ], + }; // msig address - RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM + + const mnem3 = + 'advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor'; + const { sk } = algosdk.mnemonicToSecretKey(mnem3); + + const o = { + to: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', + fee: 4, + amount: 1000, + firstRound: 12466, + lastRound: 13466, + genesisID: 'devnet-v33.0', + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + closeRemainderTo: + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA', + note: new Uint8Array(Buffer.from('X4Bl4wQ9rCo=', 'base64')), + }; + + const jsDec = algosdk.signMultisigTransaction(o, params, sk); + // this golden also contains the correct multisig address + const golden = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgaJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXiBonBrxCAJYzIJU3OJ8HVnEXc5kcfQPhtzyMT1K/av8BqiXPnCcYKicGvEIOfw+E0GgR358xyNh4sRVfRnHVGhhcIAkIZn9ElYcGihoXPEQF6nXZ7CgInd1h7NVspIPFZNhkPL+vGFpTNwH3Eh9gwPM8pf1EPTHfPvjf14sS7xN7mTK+wrz7Odhp4rdWBNUASjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQSYomZ2zTCyo2dlbqxkZXZuZXQtdjMzLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zTSapG5vdGXECF+AZeMEPawqo3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==', + 'base64' + ); + assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + + // Check txid + const txGolden = 'TDIO6RJWJIVDDJZELMSX5CPJW7MUNM3QR4YAHYAKHF3W2CFRTI7A'; + assert.deepStrictEqual(jsDec.txID, txGolden); + }); + + it('should return the same blob whether using dict-of-args or algosdk.makeFooTransaction', () => { + const params = { + version: 1, + threshold: 2, + addrs: [ + 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA', + 'BFRTECKTOOE7A5LHCF3TTEOH2A7BW46IYT2SX5VP6ANKEXHZYJY77SJTVM', + '47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU', + ], + }; // msig address - RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM + + const mnemonic = + 'advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor'; + const { sk } = algosdk.mnemonicToSecretKey(mnemonic); + + const toAddr = + 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI'; + const fromAddr = + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM'; + const fee = 4; + const amount = 1000; + const firstRound = 12466; + const lastRound = 13466; + const genesisID = 'devnet-v33.0'; + const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const closeRemainder = + 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA'; + const note = new Uint8Array(Buffer.from('X4Bl4wQ9rCo=', 'base64')); + const oDict = { + to: toAddr, + from: fromAddr, + fee, + amount, + firstRound, + lastRound, + genesisID, + genesisHash, + closeRemainderTo: closeRemainder, + note, + }; + const suggestedParams = { + genesisHash, + genesisID, + firstRound, + lastRound, + fee, + }; + const oObj = algosdk.makePaymentTxnWithSuggestedParams( + fromAddr, + toAddr, + amount, + closeRemainder, + note, + suggestedParams + ); + + const oDictOutput = algosdk.signMultisigTransaction(oDict, params, sk); + const oObjOutput = algosdk.signMultisigTransaction(oObj, params, sk); + assert.deepStrictEqual(oDictOutput.txID, oObjOutput.txID); + assert.deepStrictEqual(oDictOutput.blob, oObjOutput.blob); + }); + }); + + describe('Multisig Append', () => { + it('should return a blob that matches the go code', () => { + const params = { + version: 1, + threshold: 2, + addrs: [ + 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA', + 'BFRTECKTOOE7A5LHCF3TTEOH2A7BW46IYT2SX5VP6ANKEXHZYJY77SJTVM', + '47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU', + ], + }; + const mnem1 = + 'auction inquiry lava second expand liberty glass involve ginger illness length room item discover ahead table doctor term tackle cement bonus profit right above catch'; + const { sk } = algosdk.mnemonicToSecretKey(mnem1); + + // this is a multisig transaction with an existing signature + const o = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgaJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXiBonBrxCAJYzIJU3OJ8HVnEXc5kcfQPhtzyMT1K/av8BqiXPnCcYKicGvEIOfw+E0GgR358xyNh4sRVfRnHVGhhcIAkIZn9ElYcGihoXPEQF6nXZ7CgInd1h7NVspIPFZNhkPL+vGFpTNwH3Eh9gwPM8pf1EPTHfPvjf14sS7xN7mTK+wrz7Odhp4rdWBNUASjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQSYomZ2zTCyo2dlbqxkZXZuZXQtdjMzLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zTSapG5vdGXECF+AZeMEPawqo3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==', + 'base64' + ); + + const jsDec = algosdk.appendSignMultisigTransaction(o, params, sk); + const golden = Buffer.from( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAjmG2MILQVLoKg8q7jAYpu0r42zu9edYHrkkuSAikJAnDPplY1Pq90/ssyFhpKLrmvDDcSwNAwTGBjqtSOFYUAIGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgqJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGhc8RAXqddnsKAid3WHs1Wykg8Vk2GQ8v68YWlM3AfcSH2DA8zyl/UQ9Md8++N/XixLvE3uZMr7CvPs52Gnit1YE1QBKN0aHICoXYBo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNBJiiZnbNMLKjZ2VurGRldm5ldC12MzMuMKJnaMQgJgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dKibHbNNJqkbm90ZcQIX4Bl4wQ9rCqjcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWjcGF5', + 'base64' + ); + assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + + // Check txid + const txGolden = 'TDIO6RJWJIVDDJZELMSX5CPJW7MUNM3QR4YAHYAKHF3W2CFRTI7A'; + assert.deepStrictEqual(jsDec.txID, txGolden); + }); + }); + + describe('Multisig Address', () => { + it('should return the correct address from preimage', () => { + const params = { + version: 1, + threshold: 2, + addrs: [ + 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA', + 'BFRTECKTOOE7A5LHCF3TTEOH2A7BW46IYT2SX5VP6ANKEXHZYJY77SJTVM', + '47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU', + ], + }; + const outAddr = algosdk.multisigAddress(params); + assert.deepStrictEqual( + outAddr, + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + ); + }); + }); + + describe('Group operations', () => { + it('should return a blob that matches the go code', () => { + const address = + 'UPYAFLHSIPMJOHVXU2MPLQ46GXJKSDCEMZ6RLCQ7GWB5PRDKJUWKKXECXI'; + const [fromAddress, toAddress] = [address, address]; + const fee = 1000; + const amount = 2000; + const genesisID = 'devnet-v1.0'; + const genesisHash = 'sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E'; + const firstRound1 = 710399; + const note1 = new Uint8Array(Buffer.from('wRKw5cJ0CMo=', 'base64')); + const o1 = { + to: toAddress, + from: fromAddress, + fee, + amount, + firstRound: firstRound1, + lastRound: firstRound1 + 1000, + genesisID, + genesisHash, + note: note1, + flatFee: true, + }; + + const firstRound2 = 710515; + const note2 = new Uint8Array(Buffer.from('dBlHI6BdrIg=', 'base64')); + + const o2 = { + to: toAddress, + from: fromAddress, + fee, + amount, + firstRound: firstRound2, + lastRound: firstRound2 + 1000, + genesisID, + genesisHash, + note: note2, + flatFee: true, + }; + + const goldenTx1 = + 'gaN0eG6Ko2FtdM0H0KNmZWXNA+iiZnbOAArW/6NnZW6rZGV2bmV0LXYxLjCiZ2jEILAtz+3tknW6iiStLW4gnSvbXUqW3ul3ghinaDc5pY9Bomx2zgAK2uekbm90ZcQIwRKw5cJ0CMqjcmN2xCCj8AKs8kPYlx63ppj1w5410qkMRGZ9FYofNYPXxGpNLKNzbmTEIKPwAqzyQ9iXHremmPXDnjXSqQxEZn0Vih81g9fEak0spHR5cGWjcGF5'; + const goldenTx2 = + 'gaN0eG6Ko2FtdM0H0KNmZWXNA+iiZnbOAArXc6NnZW6rZGV2bmV0LXYxLjCiZ2jEILAtz+3tknW6iiStLW4gnSvbXUqW3ul3ghinaDc5pY9Bomx2zgAK21ukbm90ZcQIdBlHI6BdrIijcmN2xCCj8AKs8kPYlx63ppj1w5410qkMRGZ9FYofNYPXxGpNLKNzbmTEIKPwAqzyQ9iXHremmPXDnjXSqQxEZn0Vih81g9fEak0spHR5cGWjcGF5'; + + const tx1 = new algosdk.Transaction(o1); + const tx2 = new algosdk.Transaction(o2); + + // goal clerk send dumps unsigned transaction as signed with empty signature in order to save tx type + let stx1 = Buffer.from( + algosdk.encodeObj({ txn: tx1.get_obj_for_encoding() }) + ); + let stx2 = Buffer.from( + algosdk.encodeObj({ txn: tx2.get_obj_for_encoding() }) + ); + assert.deepStrictEqual(stx1, Buffer.from(goldenTx1, 'base64')); + assert.deepStrictEqual(stx2, Buffer.from(goldenTx2, 'base64')); + + // goal clerk group sets Group to every transaction and concatenate them in output file + // simulating that behavior here + const goldenTxg = + 'gaN0eG6Lo2FtdM0H0KNmZWXNA+iiZnbOAArW/6NnZW6rZGV2bmV0LXYxLjCiZ2jEILAtz+3tknW6iiStLW4gnSvbXUqW3ul3ghinaDc5pY9Bo2dycMQgLiQ9OBup9H/bZLSfQUH2S6iHUM6FQ3PLuv9FNKyt09SibHbOAAra56Rub3RlxAjBErDlwnQIyqNyY3bEIKPwAqzyQ9iXHremmPXDnjXSqQxEZn0Vih81g9fEak0so3NuZMQgo/ACrPJD2Jcet6aY9cOeNdKpDERmfRWKHzWD18RqTSykdHlwZaNwYXmBo3R4boujYW10zQfQo2ZlZc0D6KJmds4ACtdzo2dlbqtkZXZuZXQtdjEuMKJnaMQgsC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0GjZ3JwxCAuJD04G6n0f9tktJ9BQfZLqIdQzoVDc8u6/0U0rK3T1KJsds4ACttbpG5vdGXECHQZRyOgXayIo3JjdsQgo/ACrPJD2Jcet6aY9cOeNdKpDERmfRWKHzWD18RqTSyjc25kxCCj8AKs8kPYlx63ppj1w5410qkMRGZ9FYofNYPXxGpNLKR0eXBlo3BheQ=='; + { + const gid = algosdk.computeGroupID([tx1, tx2]); + tx1.group = gid; + tx2.group = gid; + stx1 = algosdk.encodeObj({ txn: tx1.get_obj_for_encoding() }); + stx2 = algosdk.encodeObj({ txn: tx2.get_obj_for_encoding() }); + const concat = Buffer.concat([stx1, stx2]); + assert.deepStrictEqual(concat, Buffer.from(goldenTxg, 'base64')); + } + + // check computeGroupID for list of dicts (not Transaction objects) + { + const gid = algosdk.computeGroupID([o1, o2]); + tx1.group = gid; + tx2.group = gid; + stx1 = algosdk.encodeObj({ txn: tx1.get_obj_for_encoding() }); + stx2 = algosdk.encodeObj({ txn: tx2.get_obj_for_encoding() }); + const concat = Buffer.concat([stx1, stx2]); + assert.deepStrictEqual(concat, Buffer.from(goldenTxg, 'base64')); + } + + // check filtering by address in assignGroupID + let result; + result = algosdk.assignGroupID([tx1, tx2]); + assert.equal(result.length, 2); + + result = algosdk.assignGroupID([tx1, tx2], ''); + assert.equal(result.length, 2); + + result = algosdk.assignGroupID([tx1, tx2], address); + assert.equal(result.length, 2); + + result = algosdk.assignGroupID( + [tx1, tx2], + 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA' + ); + assert.ok(result instanceof Array); + assert.equal(result.length, 0); + }); + }); + + describe('assets', () => { + it('should return a blob that matches the go code for asset create', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const golden = + 'gqNzaWfEQEDd1OMRoQI/rzNlU4iiF50XQXmup3k5czI9hEsNqHT7K4KsfmA/0DUVkbzOwtJdRsHS8trm3Arjpy9r7AXlbAujdHhuh6RhcGFyiaJhbcQgZkFDUE80blJnTzU1ajFuZEFLM1c2U2djNEFQa2N5RmiiYW6odGVzdGNvaW6iYXWnd2Vic2l0ZaFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFmxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFtxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFyxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aF0ZKJ1bqN0c3SjZmVlzQ+0omZ2zgAE7A+iZ2jEIEhjtRiks8hOyBDyLU8QgcsPcfBZp6wg3sYvf3DlCToiomx2zgAE7/ejc25kxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aR0eXBlpGFjZmc='; + let sk = + 'awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred'; + const createTxn = { + from: address, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetTotal: 100, + assetDefaultFrozen: false, + assetManager: address, + assetReserve: address, + assetFreeze: address, + assetClawback: address, + assetUnitName: 'tst', + assetName: 'testcoin', + assetURL: 'website', + assetMetadataHash: 'fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh', + type: 'acfg', + }; + sk = algosdk.mnemonicToSecretKey(sk); + const jsDecCreate = algosdk.signTransaction(createTxn, sk.sk); + assert.deepStrictEqual( + Buffer.from(jsDecCreate.blob), + Buffer.from(golden, 'base64') + ); + }); + + it('should return a blob that matches the go code for asset create with decimals', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const golden = + 'gqNzaWfEQCj5xLqNozR5ahB+LNBlTG+d0gl0vWBrGdAXj1ibsCkvAwOsXs5KHZK1YdLgkdJecQiWm4oiZ+pm5Yg0m3KFqgqjdHhuh6RhcGFyiqJhbcQgZkFDUE80blJnTzU1ajFuZEFLM1c2U2djNEFQa2N5RmiiYW6odGVzdGNvaW6iYXWnd2Vic2l0ZaFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aJkYwGhZsQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hbcQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hcsQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hdGSidW6jdHN0o2ZlZc0P3KJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/3o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaRhY2Zn'; + let sk = + 'awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred'; + const createTxn = { + from: address, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetTotal: 100, + assetDecimals: 1, + assetDefaultFrozen: false, + assetManager: address, + assetReserve: address, + assetFreeze: address, + assetClawback: address, + assetUnitName: 'tst', + assetName: 'testcoin', + assetURL: 'website', + assetMetadataHash: 'fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh', + type: 'acfg', + }; + sk = algosdk.mnemonicToSecretKey(sk); + const jsDecCreate = algosdk.signTransaction(createTxn, sk.sk); + assert.deepStrictEqual( + Buffer.from(jsDecCreate.blob), + Buffer.from(golden, 'base64') + ); + }); + + it('should return a blob that matches the go code for asset configuration', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const golden = + 'gqNzaWfEQBBkfw5n6UevuIMDo2lHyU4dS80JCCQ/vTRUcTx5m0ivX68zTKyuVRrHaTbxbRRc3YpJ4zeVEnC9Fiw3Wf4REwejdHhuiKRhcGFyhKFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFmxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFtxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFyxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRjYWlkzQTSo2ZlZc0NSKJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/3o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaRhY2Zn'; + let sk = + 'awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred'; + const o = { + from: address, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetIndex: 1234, + assetManager: address, + assetReserve: address, + assetFreeze: address, + assetClawback: address, + type: 'acfg', + }; + sk = algosdk.mnemonicToSecretKey(sk); + const jsDec = algosdk.signTransaction(o, sk.sk); + assert.deepStrictEqual( + Buffer.from(jsDec.blob), + Buffer.from(golden, 'base64') + ); + }); + + it('should return a blob that matches the go code for asset destroy', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const golden = + 'gqNzaWfEQBSP7HtzD/Lvn4aVvaNpeR4T93dQgo4LvywEwcZgDEoc/WVl3aKsZGcZkcRFoiWk8AidhfOZzZYutckkccB8RgGjdHhuh6RjYWlkAaNmZWXNB1iiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWkYWNmZw=='; + let sk = + 'awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred'; + const o = { + from: address, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetIndex: 1, + type: 'acfg', + }; + sk = algosdk.mnemonicToSecretKey(sk); + const jsDec = algosdk.signTransaction(o, sk.sk); + assert.deepStrictEqual( + Buffer.from(jsDec.blob), + Buffer.from(golden, 'base64') + ); + }); + it('should return a blob that matches the go code for asset freeze', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const o = { + from: addr, + fee: 10, + firstRound: 322575, + lastRound: 323576, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + type: 'afrz', + freezeAccount: addr, + assetIndex: 1, + freezeState: true, + }; + + const mnem = + 'awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred'; + const { sk } = algosdk.mnemonicToSecretKey(mnem); + const jsDec = algosdk.signTransaction(o, sk); + const golden = Buffer.from( + 'gqNzaWfEQAhru5V2Xvr19s4pGnI0aslqwY4lA2skzpYtDTAN9DKSH5+qsfQQhm4oq+9VHVj7e1rQC49S28vQZmzDTVnYDQGjdHhuiaRhZnJ6w6RmYWRkxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRmYWlkAaNmZWXNCRqiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv+KNzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWkYWZyeg==', + 'base64' + ); + assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + }); + it('should return a blob that matches the go code for asset transfer', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + + const o = { + type: 'axfer', + from: addr, + to: addr, + amount: 1, + fee: 10, + firstRound: 322575, + lastRound: 323576, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetIndex: 1, + closeRemainderTo: addr, + }; + + const mnem = + 'awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred'; + const { sk } = algosdk.mnemonicToSecretKey(mnem); + const jsDec = algosdk.signTransaction(o, sk); + const golden = Buffer.from( + 'gqNzaWfEQNkEs3WdfFq6IQKJdF1n0/hbV9waLsvojy9pM1T4fvwfMNdjGQDy+LeesuQUfQVTneJD4VfMP7zKx4OUlItbrwSjdHhuiqRhYW10AaZhY2xvc2XEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pGFyY3bEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9o2ZlZc0KvqJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/4o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaVheGZlcqR4YWlkAQ==', + 'base64' + ); + assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + }); + it('should return a blob that matches the go code for asset accept', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + + const o = { + type: 'axfer', + from: addr, + to: addr, + amount: 0, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetIndex: 1, + }; + + const mnem = + 'awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred'; + const { sk } = algosdk.mnemonicToSecretKey(mnem); + const jsDec = algosdk.signTransaction(o, sk); + const golden = Buffer.from( + 'gqNzaWfEQJ7q2rOT8Sb/wB0F87ld+1zMprxVlYqbUbe+oz0WM63FctIi+K9eYFSqT26XBZ4Rr3+VTJpBE+JLKs8nctl9hgijdHhuiKRhcmN2xCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aNmZWXNCOiiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWlYXhmZXKkeGFpZAE=', + 'base64' + ); + assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + }); + it('should return a blob that matches the go code for asset revoke', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + + const o = { + type: 'axfer', + from: addr, + to: addr, + assetRevocationTarget: addr, + amount: 1, + fee: 10, + firstRound: 322575, + lastRound: 323575, + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + assetIndex: 1, + }; + + const mnem = + 'awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred'; + const { sk } = algosdk.mnemonicToSecretKey(mnem); + const jsDec = algosdk.signTransaction(o, sk); + const golden = Buffer.from( + 'gqNzaWfEQHsgfEAmEHUxLLLR9s+Y/yq5WeoGo/jAArCbany+7ZYwExMySzAhmV7M7S8+LBtJalB4EhzEUMKmt3kNKk6+vAWjdHhuiqRhYW10AaRhcmN2xCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRhc25kxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aNmZWXNCqqiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWlYXhmZXKkeGFpZAE=', + 'base64' + ); + assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + }); + }); + + describe('LogicSig', () => { + it('should return valid logic sig object', () => { + const program = Uint8Array.from([1, 32, 1, 1, 34]); // int 1 + let lsig = new algosdk.LogicSig(program); + assert.equal(lsig.logic, program); + assert.equal(lsig.args, undefined); + assert.equal(lsig.sig, undefined); + assert.equal(lsig.msig, undefined); + + const args = [Uint8Array.from('123'), Uint8Array.from('456')]; + lsig = new algosdk.LogicSig(program, args); + assert.equal(lsig.logic, program); + assert.deepEqual(lsig.args, args); + }); + }); + describe('Single logic sig', () => { + it('should work on valid program', () => { + const program = Uint8Array.from([1, 32, 1, 1, 34]); + const keys = algosdk.generateAccount(); + const lsig = new algosdk.LogicSig(program); + lsig.sign(keys.sk); + const verified = lsig.verify(algosdk.decodeAddress(keys.addr).publicKey); + assert.equal(verified, true); + + // check serialization + const encoded = lsig.toByte(); + const decoded = algosdk.logicSigFromByte(encoded); + assert.deepStrictEqual(decoded, lsig); + }); + }); + describe('Multisig logic sig', () => { + it('should work on valid program', () => { + const program = Uint8Array.from([1, 32, 1, 1, 34]); + const lsig = new algosdk.LogicSig(program); + + const keys = algosdk.generateAccount(); + assert.throws(() => lsig.appendToMultisig(keys.sk), 'empty msig'); + + const params = { + version: 1, + threshold: 2, + addrs: [ + 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA', + 'BFRTECKTOOE7A5LHCF3TTEOH2A7BW46IYT2SX5VP6ANKEXHZYJY77SJTVM', + '47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU', + ], + }; + const outAddr = algosdk.multisigAddress(params); + const msigPk = algosdk.decodeAddress(outAddr).publicKey; + const mn1 = + 'auction inquiry lava second expand liberty glass involve ginger illness length room item discover ahead table doctor term tackle cement bonus profit right above catch'; + const mn2 = + 'since during average anxiety protect cherry club long lawsuit loan expand embark forum theory winter park twenty ball kangaroo cram burst board host ability left'; + const sk1 = algosdk.mnemonicToSecretKey(mn1); + const sk2 = algosdk.mnemonicToSecretKey(mn2); + + lsig.sign(sk1.sk, params); + + // fails on wrong key + assert.throws(() => lsig.appendToMultisig(keys.sk)); + + lsig.appendToMultisig(sk2.sk); + let verified = lsig.verify(msigPk); + assert.equal(verified, true); + + // combine sig and msig + const lsigf = new algosdk.LogicSig(program); + lsigf.sign(keys.sk); + lsig.sig = lsigf.sig; + verified = lsig.verify(msigPk); + assert.equal(verified, false); + + lsig.sig = undefined; + verified = lsig.verify(msigPk); + assert.equal(verified, true); + + // check serialization + const encoded = lsig.toByte(); + const decoded = algosdk.logicSigFromByte(encoded); + assert.deepStrictEqual(decoded, lsig); + }); + }); + + describe('LogicSig Transaction', () => { + it('should match to goal-produced logic signed tx', () => { + const fromAddress = + '47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU'; + const toAddress = + 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI'; + const mn = + 'advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor'; + const fee = 1000; + const amount = 2000; + const firstRound = 2063137; + const genesisID = 'devnet-v1.0'; + const genesisHash = 'sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E='; + const note = new Uint8Array(Buffer.from('8xMCTuLQ810=', 'base64')); + + const txn = { + to: toAddress, + from: fromAddress, + fee, + amount, + firstRound, + lastRound: firstRound + 1000, + genesisID, + genesisHash, + note, + flatFee: true, + }; + + const program = Uint8Array.from([1, 32, 1, 1, 34]); // int 1 + const args = [ + Uint8Array.from([49, 50, 51]), + Uint8Array.from([52, 53, 54]), + ]; + const lsig = new algosdk.LogicSig(program, args); + const sk = algosdk.mnemonicToSecretKey(mn); + lsig.sign(sk.sk); + + const jsDec = algosdk.signLogicSigTransaction(txn, lsig); + + // goal clerk send -o tx3 -a 2000 --fee 1000 -d ~/.algorand -w test -L sig.lsig --argb64 MTIz --argb64 NDU2 \ + // -f 47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU \ + // -t PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI + const golden = + 'gqRsc2lng6NhcmeSxAMxMjPEAzQ1NqFsxAUBIAEBIqNzaWfEQE6HXaI5K0lcq50o/y3bWOYsyw9TLi/oorZB4xaNdn1Z14351u2f6JTON478fl+JhIP4HNRRAIh/I8EWXBPpJQ2jdHhuiqNhbXTNB9CjZmVlzQPoomZ2zgAfeyGjZ2Vuq2Rldm5ldC12MS4womdoxCCwLc/t7ZJ1uookrS1uIJ0r211Klt7pd4IYp2g3OaWPQaJsds4AH38JpG5vdGXECPMTAk7i0PNdo3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaR0eXBlo3BheQ=='; + + assert.deepStrictEqual( + Buffer.from(jsDec.blob), + Buffer.from(golden, 'base64') + ); + const senderPk = algosdk.decodeAddress(fromAddress).publicKey; + const verified = lsig.verify(senderPk); + assert.equal(verified, true); + }); + }); + + describe('tealSign', () => { + const data = Buffer.from('Ux8jntyBJQarjKGF8A==', 'base64'); + const prog = Buffer.from('ASABASI=', 'base64'); + const addr = new algosdk.LogicSig(prog).address(); + + const seed = Buffer.from( + '5Pf7eGMA52qfMT4R4/vYCt7con/7U3yejkdXkrcb26Q=', + 'base64' + ); + const { publicKey: pk, secretKey: sk } = nacl.keyPairFromSeed(seed); + + it('should produce a verifiable signature', () => { + const sig = algosdk.tealSign(sk, data, addr); + + const parts = utils.concatArrays( + algosdk.decodeAddress(addr).publicKey, + data + ); + const toBeVerified = Buffer.from( + utils.concatArrays(Buffer.from('ProgData'), parts) + ); + const verified = nacl.verify(toBeVerified, sig, pk); + assert.equal(verified, true); + }); + + it('should produce a verifiable signature from a program', () => { + const sig1 = algosdk.tealSign(sk, data, addr); + const sig2 = algosdk.tealSignFromProgram(sk, data, prog); + + assert.deepStrictEqual(sig1, sig2); + }); + + it('should verify a valid signature', () => { + const sig = algosdk.tealSign(sk, data, addr); + + const verified = algosdk.verifyTealSign(data, addr, sig, pk); + assert.equal(verified, true); + }); + }); + + describe('v2 Dryrun models', () => { + const schema = new algosdk.modelsv2.ApplicationStateSchema({ + numUint: 5, + numByteSlice: 5, + }); + const acc = new algosdk.modelsv2.Account({ + address: 'UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M', + amount: 5002280000000000, + amountWithoutPendingRewards: 5000000000000000, + pendingRewards: 2280000000000, + rewardBase: 456, + rewards: 2280000000000, + round: 18241, + status: 'Online', + }); + const params = new algosdk.modelsv2.ApplicationParams({ + creator: 'UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M', + approvalProgram: 'AiABASI=', + clearStateProgram: 'AiABASI=', + localStateSchema: schema, + globalStateSchema: schema, + }); + const app = new algosdk.modelsv2.Application({ + id: 1380011588, + params, + }); + // make a raw txn + const txn = { + apsu: 'AiABASI=', + fee: 1000, + fv: 18242, + gh: 'ZIkPs8pTDxbRJsFB1yJ7gvnpDu0Q85FRkl2NCkEAQLU=', + lv: 19242, + note: 'tjpNge78JD8=', + snd: 'UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M', + type: 'appl', + }; + const req = new algosdk.modelsv2.DryrunRequest({ + accounts: [acc], + apps: [app], + round: 18241, + protocolVersion: 'future', + latestTimestamp: 1592537757, + txns: [{ txn }], + }); + + it('should be properly serialized to JSON', () => { + const actual = req.get_obj_for_encoding(); + + const golden = + 'ewogICJhY2NvdW50cyI6IFsKICAgIHsKICAgICAgImFkZHJlc3MiOiAiVUFQSkUzNTVLN0JHN1JRVk1UWk9XN1FXNElDWkpFSUMzUlpHWUc1TFNIWjY1SzZMQ05GUEpEU1I3TSIsCiAgICAgICJhbW91bnQiOiA1MDAyMjgwMDAwMDAwMDAwLAogICAgICAiYW1vdW50LXdpdGhvdXQtcGVuZGluZy1yZXdhcmRzIjogNTAwMDAwMDAwMDAwMDAwMCwKICAgICAgInBlbmRpbmctcmV3YXJkcyI6IDIyODAwMDAwMDAwMDAsCiAgICAgICJyZXdhcmQtYmFzZSI6IDQ1NiwKICAgICAgInJld2FyZHMiOiAyMjgwMDAwMDAwMDAwLAogICAgICAicm91bmQiOiAxODI0MSwKICAgICAgInN0YXR1cyI6ICJPbmxpbmUiCiAgICB9CiAgXSwKICAiYXBwcyI6IFsKICAgIHsKICAgICAgImlkIjogMTM4MDAxMTU4OCwKICAgICAgInBhcmFtcyI6IHsKICAgICAgICAiY3JlYXRvciI6ICJVQVBKRTM1NUs3Qkc3UlFWTVRaT1c3UVc0SUNaSkVJQzNSWkdZRzVMU0haNjVLNkxDTkZQSkRTUjdNIiwKICAgICAgICAiYXBwcm92YWwtcHJvZ3JhbSI6ICJBaUFCQVNJPSIsCiAgICAgICAgImNsZWFyLXN0YXRlLXByb2dyYW0iOiAiQWlBQkFTST0iLAogICAgICAgICJnbG9iYWwtc3RhdGUtc2NoZW1hIjogewogICAgICAgICAgIm51bS1ieXRlLXNsaWNlIjogNSwKICAgICAgICAgICJudW0tdWludCI6IDUKICAgICAgICB9LAogICAgICAgICJsb2NhbC1zdGF0ZS1zY2hlbWEiOiB7CiAgICAgICAgICAibnVtLWJ5dGUtc2xpY2UiOiA1LAogICAgICAgICAgIm51bS11aW50IjogNQogICAgICAgIH0KICAgICAgfQogICAgfQogIF0sCiAgImxhdGVzdC10aW1lc3RhbXAiOiAxNTkyNTM3NzU3LAogICJwcm90b2NvbC12ZXJzaW9uIjogImZ1dHVyZSIsCiAgInJvdW5kIjogMTgyNDEsCiAgInR4bnMiOiBbCiAgICB7CiAgICAgICJ0eG4iOiB7CiAgICAgICAgImFwc3UiOiAiQWlBQkFTST0iLAogICAgICAgICJmZWUiOiAxMDAwLAogICAgICAgICJmdiI6IDE4MjQyLAogICAgICAgICJnaCI6ICJaSWtQczhwVER4YlJKc0ZCMXlKN2d2bnBEdTBRODVGUmtsMk5Da0VBUUxVPSIsCiAgICAgICAgImx2IjogMTkyNDIsCiAgICAgICAgIm5vdGUiOiAidGpwTmdlNzhKRDg9IiwKICAgICAgICAic25kIjogIlVBUEpFMzU1SzdCRzdSUVZNVFpPVzdRVzRJQ1pKRUlDM1JaR1lHNUxTSFo2NUs2TENORlBKRFNSN00iLAogICAgICAgICJ0eXBlIjogImFwcGwiCiAgICAgIH0KICAgIH0KICBdCn0K'; + const goldenString = Buffer.from(golden, 'base64').toString('utf8'); + const expected = JSON.parse(goldenString); + + assert.deepStrictEqual(actual, expected); + }); + + it('should be properly serialized to msgpack', () => { + const actual = req.get_obj_for_encoding(true); + const golden = + 'hqhhY2NvdW50c5GIp2FkZHJlc3PZOlVBUEpFMzU1SzdCRzdSUVZNVFpPVzdRVzRJQ1pKRUlDM1JaR1lHNUxTSFo2NUs2TENORlBKRFNSN02mYW1vdW50zwARxYwSd5AAvmFtb3VudC13aXRob3V0LXBlbmRpbmctcmV3YXJkc88AEcN5N+CAAK9wZW5kaW5nLXJld2FyZHPPAAACEtqXEACrcmV3YXJkLWJhc2XNAcincmV3YXJkc88AAAIS2pcQAKVyb3VuZM1HQaZzdGF0dXOmT25saW5lpGFwcHORgqJpZM5SQU5EpnBhcmFtc4WwYXBwcm92YWwtcHJvZ3JhbcQFAiABASKzY2xlYXItc3RhdGUtcHJvZ3JhbcQFAiABASKnY3JlYXRvctk6VUFQSkUzNTVLN0JHN1JRVk1UWk9XN1FXNElDWkpFSUMzUlpHWUc1TFNIWjY1SzZMQ05GUEpEU1I3TbNnbG9iYWwtc3RhdGUtc2NoZW1hgq5udW0tYnl0ZS1zbGljZQWobnVtLXVpbnQFsmxvY2FsLXN0YXRlLXNjaGVtYYKubnVtLWJ5dGUtc2xpY2UFqG51bS11aW50BbBsYXRlc3QtdGltZXN0YW1wzl7sMp2wcHJvdG9jb2wtdmVyc2lvbqZmdXR1cmWlcm91bmTNR0GkdHhuc5GBo3R4boikYXBzdahBaUFCQVNJPaNmZWXNA+iiZnbNR0KiZ2jZLFpJa1BzOHBURHhiUkpzRkIxeUo3Z3ZucER1MFE4NUZSa2wyTkNrRUFRTFU9omx2zUsqpG5vdGWsdGpwTmdlNzhKRDg9o3NuZNk6VUFQSkUzNTVLN0JHN1JRVk1UWk9XN1FXNElDWkpFSUMzUlpHWUc1TFNIWjY1SzZMQ05GUEpEU1I3TaR0eXBlpGFwcGw='; + const goldenBinary = new Uint8Array(Buffer.from(golden, 'base64')); + const expected = algosdk.decodeObj(goldenBinary); + + assert.deepStrictEqual(actual, expected); + }); + }); +}); diff --git a/tests/8.LogicSig.ts b/tests/8.LogicSig.ts new file mode 100644 index 0000000..f9c38a3 --- /dev/null +++ b/tests/8.LogicSig.ts @@ -0,0 +1,721 @@ +/* eslint-env mocha */ +import { Buffer } from 'buffer'; +import assert from 'assert'; +import algosdk from '../src/index'; + +const sampleAccount1 = algosdk.mnemonicToSecretKey( + 'auction inquiry lava second expand liberty glass involve ginger illness length room item discover ahead table doctor term tackle cement bonus profit right above catch' +); +const sampleAccount2 = algosdk.mnemonicToSecretKey( + 'since during average anxiety protect cherry club long lawsuit loan expand embark forum theory winter park twenty ball kangaroo cram burst board host ability left' +); +const sampleAccount3 = algosdk.mnemonicToSecretKey( + 'advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor' +); + +// Multisig Golden Params +const sampleMultisigParams: algosdk.MultisigMetadata = { + version: 1, + threshold: 2, + addrs: [sampleAccount1.addr, sampleAccount2.addr, sampleAccount3.addr], +}; + +const sampleMultisigAddr = algosdk.multisigAddress(sampleMultisigParams); + +describe('LogicSig', () => { + describe('makeLogicSig', () => { + it('should work on valid program', () => { + const program = Uint8Array.from([1, 32, 1, 1, 34]); + const programHash = + '6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY'; + const pk = algosdk.decodeAddress(programHash).publicKey; + let lsig = new algosdk.LogicSig(program); + assert.strictEqual(lsig.logic, program); + assert.strictEqual(lsig.args, undefined); + assert.strictEqual(lsig.sig, undefined); + assert.strictEqual(lsig.msig, undefined); + assert.strictEqual(lsig.address(), programHash); + + let verified = lsig.verify(pk); + assert.strictEqual(verified, true); + + const args = [Uint8Array.from([1, 2, 3]), Uint8Array.from([4, 5, 6])]; + lsig = new algosdk.LogicSig(program, args); + assert.strictEqual(lsig.logic, program); + assert.deepStrictEqual(lsig.args, args); + assert.strictEqual(lsig.sig, undefined); + assert.strictEqual(lsig.msig, undefined); + + verified = lsig.verify(pk); + assert.strictEqual(verified, true); + + // check serialization + const encoded = lsig.toByte(); + const decoded = algosdk.logicSigFromByte(encoded); + assert.deepStrictEqual(decoded, lsig); + }); + it('should fail on tampered program', () => { + const program = Uint8Array.from([1, 32, 1, 1, 34]); + const programHash = + '6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY'; + const pk = algosdk.decodeAddress(programHash).publicKey; + + program[3] = 2; + const lsig = new algosdk.LogicSig(program); + const verified = lsig.verify(pk); + assert.strictEqual(verified, false); + }); + }); + + describe('address', () => { + it('should produce the correct address', () => { + const program = Uint8Array.from([1, 32, 1, 1, 34]); + const programHash = + '6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY'; + + const lsig = new algosdk.LogicSig(program); + const address = lsig.address(); + + assert.deepStrictEqual(address, programHash); + }); + }); +}); + +describe('LogicSigAccount', () => { + describe('constructor', () => { + it('should work on valid program without args', () => { + const program = Uint8Array.from([1, 32, 1, 1, 34]); + + const lsigAccount = new algosdk.LogicSigAccount(program); + assert.deepStrictEqual(lsigAccount.lsig.logic, program); + assert.strictEqual(lsigAccount.lsig.args, undefined); + assert.strictEqual(lsigAccount.lsig.sig, undefined); + assert.strictEqual(lsigAccount.lsig.msig, undefined); + assert.strictEqual(lsigAccount.sigkey, undefined); + + // check serialization + const encoded = lsigAccount.toByte(); + const expectedEncoded = new Uint8Array( + Buffer.from('gaRsc2lngaFsxAUBIAEBIg==', 'base64') + ); + assert.deepStrictEqual(encoded, expectedEncoded); + + const decoded = algosdk.LogicSigAccount.fromByte(encoded); + assert.deepStrictEqual(decoded, lsigAccount); + }); + it('should work on valid program with args', () => { + const program = Uint8Array.from([1, 32, 1, 1, 34]); + const args = [Uint8Array.from([1]), Uint8Array.from([2, 3])]; + + const lsigAccount = new algosdk.LogicSigAccount(program, args); + assert.deepStrictEqual(lsigAccount.lsig.logic, program); + assert.deepStrictEqual(lsigAccount.lsig.args, args); + assert.strictEqual(lsigAccount.lsig.sig, undefined); + assert.strictEqual(lsigAccount.lsig.msig, undefined); + assert.strictEqual(lsigAccount.sigkey, undefined); + + // check serialization + const encoded = lsigAccount.toByte(); + const expectedEncoded = new Uint8Array( + Buffer.from('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==', 'base64') + ); + assert.deepStrictEqual(encoded, expectedEncoded); + + const decoded = algosdk.LogicSigAccount.fromByte(encoded); + assert.deepStrictEqual(decoded, lsigAccount); + }); + }); + + describe('sign', () => { + it('should properly sign the program', () => { + const program = Uint8Array.from([1, 32, 1, 1, 34]); + const args = [Uint8Array.from([1]), Uint8Array.from([2, 3])]; + + const lsigAccount = new algosdk.LogicSigAccount(program, args); + lsigAccount.sign(sampleAccount1.sk); + + const expectedSig = new Uint8Array( + Buffer.from( + 'SRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9Ag==', + 'base64' + ) + ); + const expectedSigKey = algosdk.decodeAddress(sampleAccount1.addr) + .publicKey; + + assert.deepStrictEqual(lsigAccount.lsig.logic, program); + assert.deepStrictEqual(lsigAccount.lsig.args, args); + assert.deepStrictEqual(lsigAccount.lsig.sig, expectedSig); + assert.strictEqual(lsigAccount.lsig.msig, undefined); + assert.deepStrictEqual(lsigAccount.sigkey, expectedSigKey); + + // check serialization + const encoded = lsigAccount.toByte(); + const expectedEncoded = new Uint8Array( + Buffer.from( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==', + 'base64' + ) + ); + assert.deepStrictEqual(encoded, expectedEncoded); + + const decoded = algosdk.LogicSigAccount.fromByte(encoded); + assert.deepStrictEqual(decoded, lsigAccount); + }); + }); + + describe('signMultisig', () => { + it('should properly sign the program', () => { + const program = Uint8Array.from([1, 32, 1, 1, 34]); + const args = [Uint8Array.from([1]), Uint8Array.from([2, 3])]; + + const lsigAccount = new algosdk.LogicSigAccount(program, args); + lsigAccount.signMultisig(sampleMultisigParams, sampleAccount1.sk); + + const expectedSig = new Uint8Array( + Buffer.from( + 'SRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9Ag==', + 'base64' + ) + ); + const expectedMsig: algosdk.EncodedMultisig = { + v: sampleMultisigParams.version, + thr: sampleMultisigParams.threshold, + subsig: sampleMultisigParams.addrs.map((addr) => ({ + pk: algosdk.decodeAddress(addr).publicKey, + })), + }; + expectedMsig.subsig[0].s = expectedSig; + + assert.deepStrictEqual(lsigAccount.lsig.logic, program); + assert.deepStrictEqual(lsigAccount.lsig.args, args); + assert.strictEqual(lsigAccount.lsig.sig, undefined); + assert.deepStrictEqual(lsigAccount.lsig.msig, expectedMsig); + assert.strictEqual(lsigAccount.sigkey, undefined); + + // check serialization + const encoded = lsigAccount.toByte(); + const expectedEncoded = new Uint8Array( + Buffer.from( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AQ==', + 'base64' + ) + ); + assert.deepStrictEqual(encoded, expectedEncoded); + + const decoded = algosdk.LogicSigAccount.fromByte(encoded); + assert.deepStrictEqual(decoded, lsigAccount); + }); + }); + + describe('appendToMultisig', () => { + it('should properly append a signature', () => { + const msig1of3Encoded = new Uint8Array( + Buffer.from( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AQ==', + 'base64' + ) + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(msig1of3Encoded); + + lsigAccount.appendToMultisig(sampleAccount2.sk); + + const expectedSig1 = new Uint8Array( + Buffer.from( + 'SRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9Ag==', + 'base64' + ) + ); + const expectedSig2 = new Uint8Array( + Buffer.from( + 'ZLxV2+2RokHUKrZg9+FKuZmaUrOxcVjO/D9P58siQRStqT1ehAUCChemaYMDIk6Go4tqNsVUviBQ/9PuqLMECQ==', + 'base64' + ) + ); + const expectedMsig: algosdk.EncodedMultisig = { + v: sampleMultisigParams.version, + thr: sampleMultisigParams.threshold, + subsig: sampleMultisigParams.addrs.map((addr) => ({ + pk: algosdk.decodeAddress(addr).publicKey, + })), + }; + expectedMsig.subsig[0].s = expectedSig1; + expectedMsig.subsig[1].s = expectedSig2; + + assert.strictEqual(lsigAccount.lsig.sig, undefined); + assert.deepStrictEqual(lsigAccount.lsig.msig, expectedMsig); + assert.strictEqual(lsigAccount.sigkey, undefined); + + // check serialization + const encoded = lsigAccount.toByte(); + const expectedEncoded = new Uint8Array( + Buffer.from( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYB', + 'base64' + ) + ); + assert.deepStrictEqual(encoded, expectedEncoded); + + const decoded = algosdk.LogicSigAccount.fromByte(encoded); + assert.deepStrictEqual(decoded, lsigAccount); + }); + }); + + describe('verify', () => { + it('should verify valid escrow', () => { + const escrowEncoded = new Uint8Array( + Buffer.from('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==', 'base64') + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(escrowEncoded); + + assert.strictEqual(lsigAccount.verify(), true); + }); + + it('should verify valid single sig', () => { + const sigEncoded = new Uint8Array( + Buffer.from( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==', + 'base64' + ) + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(sigEncoded); + + assert.strictEqual(lsigAccount.verify(), true); + }); + + it('should fail single sig with wrong sig', () => { + const sigEncoded = new Uint8Array( + Buffer.from( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==', + 'base64' + ) + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(sigEncoded); + + // modify signature + lsigAccount.lsig.sig![0] = 0; + + assert.strictEqual(lsigAccount.verify(), false); + }); + + it('should verify valid multisig', () => { + const msigEncoded = new Uint8Array( + Buffer.from( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYB', + 'base64' + ) + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(msigEncoded); + + assert.strictEqual(lsigAccount.verify(), true); + }); + + it('should fail multisig with wrong sig', () => { + const msigEncoded = new Uint8Array( + Buffer.from( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYB', + 'base64' + ) + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(msigEncoded); + + // modify signature + lsigAccount.lsig.msig!.subsig[0].s[0] = 0; + + assert.strictEqual(lsigAccount.verify(), false); + }); + + it('should fail multisig that does not meet threshold', () => { + const msigBelowThresholdEncoded = new Uint8Array( + Buffer.from( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AQ==', + 'base64' + ) + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte( + msigBelowThresholdEncoded + ); + + assert.strictEqual(lsigAccount.verify(), false); + }); + }); + + describe('isDelegated', () => { + it('should be correct for escrow', () => { + const escrowEncoded = new Uint8Array( + Buffer.from('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==', 'base64') + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(escrowEncoded); + + assert.strictEqual(lsigAccount.isDelegated(), false); + }); + + it('should be correct for single sig', () => { + const sigEncoded = new Uint8Array( + Buffer.from( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==', + 'base64' + ) + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(sigEncoded); + + assert.strictEqual(lsigAccount.isDelegated(), true); + }); + + it('should be correct for multisig', () => { + const msigEncoded = new Uint8Array( + Buffer.from( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYB', + 'base64' + ) + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(msigEncoded); + + assert.strictEqual(lsigAccount.isDelegated(), true); + }); + }); + + describe('address', () => { + it('should be correct for escrow', () => { + const escrowEncoded = new Uint8Array( + Buffer.from('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==', 'base64') + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(escrowEncoded); + + const addr = lsigAccount.address(); + + const expectedAddr = + '6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY'; + + assert.strictEqual(addr, expectedAddr); + }); + + it('should be correct for single sig', () => { + const sigEncoded = new Uint8Array( + Buffer.from( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==', + 'base64' + ) + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(sigEncoded); + + const addr = lsigAccount.address(); + + const expectedAddr = + 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA'; + + assert.strictEqual(addr, expectedAddr); + }); + + it('should be correct for multisig', () => { + const msigEncoded = new Uint8Array( + Buffer.from( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYB', + 'base64' + ) + ); + const lsigAccount = algosdk.LogicSigAccount.fromByte(msigEncoded); + + const addr = lsigAccount.address(); + + const expectedAddr = + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM'; + + assert.strictEqual(addr, expectedAddr); + }); + }); +}); + +describe('signLogicSigTransaction', () => { + const program = Uint8Array.from([1, 32, 1, 1, 34]); + const args = [Uint8Array.from([1]), Uint8Array.from([2, 3])]; + + const otherAddr = + 'WTDCE2FEYM2VB5MKNXKLRSRDTSPR2EFTIGVH4GRW4PHGD6747GFJTBGT2A'; + + function testSign( + lsigObject: algosdk.LogicSig | algosdk.LogicSigAccount, + sender: string, + expected: { txID: string; blob: Uint8Array } + ) { + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: sender, + to: otherAddr, + amount: 5000, + suggestedParams: { + flatFee: true, + fee: 217000, + firstRound: 972508, + lastRound: 973508, + genesisID: 'testnet-v31.0', + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + }, + note: new Uint8Array([180, 81, 121, 57, 252, 250, 210, 113]), + }); + + const actual = algosdk.signLogicSigTransaction(txn, lsigObject); + + assert.deepStrictEqual(actual, expected); + } + + describe('with LogicSig', () => { + describe('escrow', () => { + const lsig = new algosdk.LogicSig(program, args); + + it('should match expected when sender is LogicSig address', () => { + const sender = lsig.address(); + const expected = { + txID: 'SV3GD4AKRRX43F3V4V7GYYB6YCQEPULGUI6GKZO6GPJDKOO75NFA', + blob: new Uint8Array( + Buffer.from( + 'gqRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraR0eXBlo3BheQ==', + 'base64' + ) + ), + }; + testSign(lsig, sender, expected); + }); + + it('should match expected when sender is not LogicSig address', () => { + const sender = otherAddr; + const expected = { + txID: 'DRBC5KBOYEUCL6L6H45GQSRKCCUTPNELUHUSQO4ZWCEODJEXQBBQ', + blob: new Uint8Array( + Buffer.from( + 'g6Rsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqRzZ25yxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqR0eXBlo3BheQ==', + 'base64' + ) + ), + }; + testSign(lsig, sender, expected); + }); + }); + + describe('single sig', () => { + const account = algosdk.mnemonicToSecretKey( + 'olympic cricket tower model share zone grid twist sponsor avoid eight apology patient party success claim famous rapid donor pledge bomb mystery security ability often' + ); + const lsig = new algosdk.LogicSig(program, args); + lsig.sign(account.sk); + + it('should match expected when sender is LogicSig address', () => { + const sender = account.addr; + const expected = { + txID: 'EZB2N2TEFR5OOL76Z46ZMRUL3ZQQOYKRFIX6WSHQ5FWESHU4LZPA', + blob: new Uint8Array( + Buffer.from( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQD4FPTlN+xK8ZXmf6jGKe46iUYtVLIq+bNenZS3YsBh+IQUtuSRiiRblYXTNDxmsuWxFpCmRmREd5Hzk/BLszgKjdHhuiqNhbXTNE4ijZmVlzgADT6iiZnbOAA7W3KNnZW6tdGVzdG5ldC12MzEuMKJnaMQgJgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dKibHbOAA7axKRub3RlxAi0UXk5/PrScaNyY3bEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKo3NuZMQgXmdPHAru7DdxiY9hx2/10koZeT4skfoIUWJj44Vz6kKkdHlwZaNwYXk=', + 'base64' + ) + ), + }; + testSign(lsig, sender, expected); + }); + + it('should throw an error when sender is not LogicSig address', () => { + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: otherAddr, + to: otherAddr, + amount: 5000, + suggestedParams: { + flatFee: true, + fee: 217000, + firstRound: 972508, + lastRound: 973508, + genesisID: 'testnet-v31.0', + genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + }, + note: new Uint8Array([180, 81, 121, 57, 252, 250, 210, 113]), + }); + + assert.throws( + () => algosdk.signLogicSigTransaction(txn, lsig), + (err) => + err.message === + 'Logic signature verification failed. Ensure the program and signature are valid.' + ); + }); + }); + + describe('multisig', () => { + const lsig = new algosdk.LogicSig(program, args); + lsig.sign(sampleAccount1.sk, sampleMultisigParams); + lsig.appendToMultisig(sampleAccount2.sk); + + it('should match expected when sender is LogicSig address', () => { + const sender = sampleMultisigAddr; + const expected = { + txID: 'UGGT5EZXG2OBPGWTEINC65UXIQ6UVAAOTNKRRCRAUCZH4FWJTVQQ', + blob: new Uint8Array( + Buffer.from( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBo3R4boqjYW10zROIo2ZlZc4AA0+oomZ2zgAO1tyjZ2VurXRlc3RuZXQtdjMxLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zgAO2sSkbm90ZcQItFF5Ofz60nGjcmN2xCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWjcGF5', + 'base64' + ) + ), + }; + testSign(lsig, sender, expected); + }); + + it('should match expected when sender is not LogicSig address', () => { + const sender = otherAddr; + const expected = { + txID: 'DRBC5KBOYEUCL6L6H45GQSRKCCUTPNELUHUSQO4ZWCEODJEXQBBQ', + blob: new Uint8Array( + Buffer.from( + 'g6Rsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBpHNnbnLEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfpo3R4boqjYW10zROIo2ZlZc4AA0+oomZ2zgAO1tyjZ2VurXRlc3RuZXQtdjMxLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zgAO2sSkbm90ZcQItFF5Ofz60nGjcmN2xCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqNzbmTEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKpHR5cGWjcGF5', + 'base64' + ) + ), + }; + testSign(lsig, sender, expected); + }); + }); + }); + + describe('with LogicSigAccount', () => { + describe('escrow', () => { + const lsigAccount = new algosdk.LogicSigAccount(program, args); + + it('should match expected when sender is LogicSig address', () => { + const sender = lsigAccount.address(); + const expected = { + txID: 'SV3GD4AKRRX43F3V4V7GYYB6YCQEPULGUI6GKZO6GPJDKOO75NFA', + blob: new Uint8Array( + Buffer.from( + 'gqRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraR0eXBlo3BheQ==', + 'base64' + ) + ), + }; + testSign(lsigAccount, sender, expected); + }); + + it('should match expected when sender is not LogicSig address', () => { + const sender = otherAddr; + const expected = { + txID: 'DRBC5KBOYEUCL6L6H45GQSRKCCUTPNELUHUSQO4ZWCEODJEXQBBQ', + blob: new Uint8Array( + Buffer.from( + 'g6Rsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqRzZ25yxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqR0eXBlo3BheQ==', + 'base64' + ) + ), + }; + testSign(lsigAccount, sender, expected); + }); + }); + + describe('single sig', () => { + const account = algosdk.mnemonicToSecretKey( + 'olympic cricket tower model share zone grid twist sponsor avoid eight apology patient party success claim famous rapid donor pledge bomb mystery security ability often' + ); + const lsigAccount = new algosdk.LogicSigAccount(program, args); + lsigAccount.sign(account.sk); + + it('should match expected when sender is LogicSig address', () => { + const sender = account.addr; + const expected = { + txID: 'EZB2N2TEFR5OOL76Z46ZMRUL3ZQQOYKRFIX6WSHQ5FWESHU4LZPA', + blob: new Uint8Array( + Buffer.from( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQD4FPTlN+xK8ZXmf6jGKe46iUYtVLIq+bNenZS3YsBh+IQUtuSRiiRblYXTNDxmsuWxFpCmRmREd5Hzk/BLszgKjdHhuiqNhbXTNE4ijZmVlzgADT6iiZnbOAA7W3KNnZW6tdGVzdG5ldC12MzEuMKJnaMQgJgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dKibHbOAA7axKRub3RlxAi0UXk5/PrScaNyY3bEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKo3NuZMQgXmdPHAru7DdxiY9hx2/10koZeT4skfoIUWJj44Vz6kKkdHlwZaNwYXk=', + 'base64' + ) + ), + }; + testSign(lsigAccount, sender, expected); + }); + + it('should match expected when sender is not LogicSig address', () => { + const sender = otherAddr; + const expected = { + txID: 'DRBC5KBOYEUCL6L6H45GQSRKCCUTPNELUHUSQO4ZWCEODJEXQBBQ', + blob: new Uint8Array( + Buffer.from( + 'g6Rsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQD4FPTlN+xK8ZXmf6jGKe46iUYtVLIq+bNenZS3YsBh+IQUtuSRiiRblYXTNDxmsuWxFpCmRmREd5Hzk/BLszgKkc2ducsQgXmdPHAru7DdxiY9hx2/10koZeT4skfoIUWJj44Vz6kKjdHhuiqNhbXTNE4ijZmVlzgADT6iiZnbOAA7W3KNnZW6tdGVzdG5ldC12MzEuMKJnaMQgJgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dKibHbOAA7axKRub3RlxAi0UXk5/PrScaNyY3bEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKo3NuZMQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+YqkdHlwZaNwYXk=', + 'base64' + ) + ), + }; + testSign(lsigAccount, sender, expected); + }); + }); + + describe('multisig', () => { + const lsigAccount = new algosdk.LogicSigAccount(program, args); + lsigAccount.signMultisig(sampleMultisigParams, sampleAccount1.sk); + lsigAccount.appendToMultisig(sampleAccount2.sk); + + it('should match expected when sender is LogicSig address', () => { + const sender = sampleMultisigAddr; + const expected = { + txID: 'UGGT5EZXG2OBPGWTEINC65UXIQ6UVAAOTNKRRCRAUCZH4FWJTVQQ', + blob: new Uint8Array( + Buffer.from( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBo3R4boqjYW10zROIo2ZlZc4AA0+oomZ2zgAO1tyjZ2VurXRlc3RuZXQtdjMxLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zgAO2sSkbm90ZcQItFF5Ofz60nGjcmN2xCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWjcGF5', + 'base64' + ) + ), + }; + testSign(lsigAccount, sender, expected); + }); + + it('should match expected when sender is not LogicSig address', () => { + const sender = otherAddr; + const expected = { + txID: 'DRBC5KBOYEUCL6L6H45GQSRKCCUTPNELUHUSQO4ZWCEODJEXQBBQ', + blob: new Uint8Array( + Buffer.from( + 'g6Rsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBpHNnbnLEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfpo3R4boqjYW10zROIo2ZlZc4AA0+oomZ2zgAO1tyjZ2VurXRlc3RuZXQtdjMxLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zgAO2sSkbm90ZcQItFF5Ofz60nGjcmN2xCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqNzbmTEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKpHR5cGWjcGF5', + 'base64' + ) + ), + }; + testSign(lsigAccount, sender, expected); + }); + }); + }); + + it('should sign a raw transaction object', () => { + const lsig = new algosdk.LogicSig(program); + + const from = lsig.address(); + const to = 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM'; + const fee = 10; + const amount = 847; + const firstRound = 51; + const lastRound = 61; + const note = new Uint8Array([123, 12, 200]); + const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const genesisID = ''; + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + let closeRemainderTo; + const txn = { + from, + to, + fee, + amount, + closeRemainderTo, + firstRound, + lastRound, + note, + genesisHash, + genesisID, + reKeyTo: rekeyTo, + }; + + const actual = algosdk.signLogicSigTransaction(txn, lsig); + const expected = { + txID: 'D7H6THOHOCEWJYNWMKHVOR2W36KAJXSGG6DMNTHTBWONBCG4XATA', + blob: new Uint8Array( + Buffer.from( + 'gqRsc2lngaFsxAUBIAEBIqN0eG6Ko2FtdM0DT6NmZWXNCniiZnYzomdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsdj2kbm90ZcQDewzIo3JjdsQgoImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX6lcmVrZXnEIDAhUOuXI/Dnhg1MAE4rbltxOOB+7lUduJbsxucZf2DUo3NuZMQg9nYtrHWxmX1sLJYYBoBQdJDXlREv/n+3YLJzivnH8a2kdHlwZaNwYXk=', + 'base64' + ) + ), + }; + + assert.deepStrictEqual(actual, expected); + }); +}); diff --git a/tests/9.Client.ts b/tests/9.Client.ts new file mode 100644 index 0000000..f47e368 --- /dev/null +++ b/tests/9.Client.ts @@ -0,0 +1,177 @@ +/* eslint-env mocha */ +import assert from 'assert'; +import HTTPClient from '../src/client/client'; +import { URLTokenBaseHTTPClient } from '../src/client/urlTokenBaseHTTPClient'; +import IntDecoding from '../src/types/intDecoding'; +import AlgodClient from '../src/client/v2/algod/algod'; +import * as utils from '../src/utils/utils'; + +describe('client', () => { + describe('url construction', () => { + /* eslint-disable dot-notation */ + it('should work with trivial paths', () => { + const client = new URLTokenBaseHTTPClient({}, 'http://localhost'); + const actual = client['getURL']('/relative'); + const expected = 'http://localhost/relative'; + + assert.strictEqual(actual, expected); + }); + + it('should work with number ports and trivial paths', () => { + const client = new URLTokenBaseHTTPClient({}, 'http://localhost', 3000); + const actual = client['getURL']('/relative'); + const expected = 'http://localhost:3000/relative'; + + assert.strictEqual(actual, expected); + }); + + it('should work with complex base URLs and complex paths', () => { + const client = new URLTokenBaseHTTPClient( + {}, + 'https://testnet-algorand.api.purestake.io/ps2/', + 8080 + ); + const actual = client['getURL']('/relative?with=query'); + const expected = + 'https://testnet-algorand.api.purestake.io:8080/ps2/relative?with=query'; + assert.strictEqual(actual, expected); + }); + + it('should work with search params', () => { + const client = new URLTokenBaseHTTPClient({}, 'http://localhost', 3000); + const actual = client['getURL']('/relative', { + format: 'json', + abc: 'xyz', + l: '2', + }); + const expected = 'http://localhost:3000/relative?format=json&abc=xyz&l=2'; + + assert.strictEqual(actual, expected); + }); + + it('should work with search params when the requested URL already has search params', () => { + const client = new URLTokenBaseHTTPClient( + {}, + 'https://testnet-algorand.api.purestake.io/ps2/', + 8080 + ); + const actual = client['getURL']('/relative?with=query', { + format: 'json', + abc: 'xyz', + l: '2', + }); + const expected = + 'https://testnet-algorand.api.purestake.io:8080/ps2/relative?with=query&format=json&abc=xyz&l=2'; + assert.strictEqual(actual, expected); + }); + + it('should encode and decode values correctly', () => { + const j = '{"total":18446744073709551615, "base":42}'; + + let options = { + // intDecoding: IntDecoding.DEFAULT, + }; + let actual = HTTPClient.parseJSON(j, 200, options); + let expected = JSON.parse(j); + assert.strictEqual(actual.total, expected.total); + assert.strictEqual(typeof actual.total, 'number'); + + options = { + intDecoding: IntDecoding.BIGINT, + }; + actual = HTTPClient.parseJSON(j, 200, options); + expected = utils.parseJSON(j, options); + assert.strictEqual(actual.total, expected.total); + assert.strictEqual(typeof actual.total, 'bigint'); + + options = { + intDecoding: IntDecoding.MIXED, + }; + actual = HTTPClient.parseJSON(j, 200, options); + expected = utils.parseJSON(j, options); + assert.strictEqual(actual.total, expected.total); + assert.strictEqual(typeof actual.total, 'bigint'); + assert.strictEqual(typeof actual.base, 'number'); + + options = { + intDecoding: IntDecoding.SAFE, + }; + assert.throws(() => HTTPClient.parseJSON(j, 200, options), Error); + }); + + it('should handle slash variations on complex paths', () => { + const regularBase = 'https://localhost/absolute'; + const regularBaseWithFinalSlash = `${regularBase}/`; + const client = new URLTokenBaseHTTPClient({}, regularBase); + const clientWithSlash = new URLTokenBaseHTTPClient( + {}, + regularBaseWithFinalSlash + ); + + const relativePath = 'relative'; + const relativePathWithInitialSlash = `/${relativePath}`; + + const clients = [client, clientWithSlash]; + const relativePaths = [relativePath, relativePathWithInitialSlash]; + + const expected = 'https://localhost/absolute/relative'; + + for (const c of clients) { + for (const p of relativePaths) { + const actual = c['getURL'](p); + assert.strictEqual(actual, expected); + } + } + }); + + it('should throw an error if protocol is the empty', () => { + const baseServer = 'localhost'; // should be http://localhost + + assert.throws(() => new URLTokenBaseHTTPClient({}, baseServer)); + }); + /* eslint-enable dot-notation */ + }); + describe('HTTPClient construction', () => { + it('should throw an error if protocol is the empty', () => { + const baseServer = 'localhost'; // should be http://localhost + + assert.throws(() => new HTTPClient({}, baseServer)); + }); + }); + describe('AlgodClient construction', () => { + /* eslint-disable dot-notation */ + const baseServer = 'http://localhost'; + it('should not assign a bogus port', () => { + const client = new AlgodClient('', baseServer); + assert.strictEqual(client.c['bc']['baseURL']['port'], ''); + }); + + it('should accept a port as an argument and assign it correctly', () => { + const client = new AlgodClient('', baseServer, 123); + assert.strictEqual(client.c['bc']['baseURL']['port'], '123'); + }); + + it('should accept a port in the url assign it correctly', () => { + const client = new AlgodClient('', `${baseServer}:${123}`); + assert.strictEqual(client.c['bc']['baseURL']['port'], '123'); + }); + + it('should override the port from the URL with the one specified in the argument', () => { + const client = new AlgodClient('', `${baseServer}:${123}`, 456); + assert.strictEqual(client.c['bc']['baseURL']['port'], '456'); + }); + + it('should not provide auth request headers when the token is empty', () => { + const client = new AlgodClient('', `${baseServer}:${123}`, 456); + assert.deepStrictEqual( + { + ...client.c['bc']['tokenHeaders'], + ...client.c['bc']['defaultHeaders'], + }, + {} + ); + }); + + /* eslint-disable dot-notation */ + }); +}); diff --git a/tests/browser/index.html b/tests/browser/index.html new file mode 100644 index 0000000..b521af9 --- /dev/null +++ b/tests/browser/index.html @@ -0,0 +1,22 @@ + + + + Algosdk Mocha Browser Testing + + + + +

Algosdk Mocha Browser Testing

+
+ + + + + + + diff --git a/tests/compile/basic.ts b/tests/compile/basic.ts new file mode 100644 index 0000000..28b6b2c --- /dev/null +++ b/tests/compile/basic.ts @@ -0,0 +1,4 @@ +/// +/* eslint-disable */ + +import * as algosdk from '../../dist/esm/index'; diff --git a/tests/compile/tsconfig.json b/tests/compile/tsconfig.json new file mode 100644 index 0000000..0d42832 --- /dev/null +++ b/tests/compile/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "noEmit": true + }, + "include": ["./basic.ts"] +} diff --git a/tests/cucumber/browser/index.html b/tests/cucumber/browser/index.html new file mode 100644 index 0000000..b82de74 --- /dev/null +++ b/tests/cucumber/browser/index.html @@ -0,0 +1,16 @@ + + + + Algosdk Browser Testing + + + + + + +

Algosdk Browser Testing

+ + diff --git a/tests/cucumber/browser/test.js b/tests/cucumber/browser/test.js new file mode 100644 index 0000000..e276efc --- /dev/null +++ b/tests/cucumber/browser/test.js @@ -0,0 +1,76 @@ +/* eslint-env browser */ +const { Buffer } = require('buffer'); +const assert = require('assert'); +const sha512 = require('js-sha512'); +const nacl = require('tweetnacl'); + +window.assert = assert; +window.Buffer = Buffer; + +window.keyPairFromSecretKey = function keyPairFromSecretKey(sk) { + return nacl.sign.keyPair.fromSecretKey(sk); +}; + +window.keyPairFromSeed = function keyPairFromSeed(seed) { + return nacl.sign.keyPair.fromSeed(seed); +}; + +window.genericHash = function genericHash(toHash) { + return sha512.sha512_256.array(toHash); +}; + +window.loadResource = async function loadResource(resource) { + const res = await fetch(`/features/resources/${resource}`); + if (!res.ok) { + throw new Error(`Failed to load resource (${res.status}): ${resource}`); + } + + return Buffer.from(await res.arrayBuffer()); +}; + +window.steps = { + given: {}, + when: {}, + then: {}, +}; + +window.getStep = function getStep(type, name) { + if (window.steps[type] == null || window.steps[type][name] == null) { + throw new Error(`Unrecognized test: ${type} ${name}`); + } + return window.steps[type][name]; +}; + +window.testWorld = {}; + +window.makeUint8Array = function makeUint8Array(arg) { + return new Uint8Array(arg); +}; + +window.makeABIMethod = function makeABIMethod(arg) { + return new window.algosdk.ABIMethod(arg); +}; + +window.makeABIContract = function makeABIContract(arg) { + return new window.algosdk.ABIContract(arg); +}; + +window.makeArray = function makeArray(...args) { + return args; +}; + +window.makeObject = function makeObject(obj) { + return { ...obj }; +}; + +window.parseJSON = function parseJSON(json) { + return JSON.parse(json); +}; + +window.formatIncludeAll = function formatIncludeAll(includeAll) { + if (!['true', 'false'].includes(includeAll)) { + throw new Error(`Unknown value for includeAll: ${includeAll}`); + } + + return includeAll === 'true'; +}; diff --git a/tests/cucumber/browser/webpack.config.js b/tests/cucumber/browser/webpack.config.js new file mode 100644 index 0000000..d39c543 --- /dev/null +++ b/tests/cucumber/browser/webpack.config.js @@ -0,0 +1,10 @@ +const path = require('path'); + +module.exports = { + entry: path.resolve(__dirname, 'test.js'), + output: { + filename: 'test.js', + path: path.resolve(__dirname, 'build'), + }, + devtool: 'source-map', +}; diff --git a/tests/cucumber/cucumber.js b/tests/cucumber/cucumber.js new file mode 100644 index 0000000..653278e --- /dev/null +++ b/tests/cucumber/cucumber.js @@ -0,0 +1,3 @@ +module.exports = { + default: '--format-options \'{"snippetInterface": "synchronous"}\'', +}; diff --git a/tests/cucumber/docker/Dockerfile b/tests/cucumber/docker/Dockerfile new file mode 100644 index 0000000..6ed2a12 --- /dev/null +++ b/tests/cucumber/docker/Dockerfile @@ -0,0 +1,35 @@ +FROM ubuntu:bionic + +# install wget, gnupg2, make +RUN apt-get update -qqy \ + && apt-get -qqy install wget gnupg2 make + +# install chrome, firefox +# based on https://github.com/SeleniumHQ/docker-selenium/blob/trunk/NodeChrome/Dockerfile +RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ + && echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list \ + && apt-get update -qqy \ + && apt-get -qqy --no-install-recommends install google-chrome-stable firefox + +# install node +RUN wget -q -O - https://deb.nodesource.com/setup_16.x | bash \ + && apt-get -qqy --no-install-recommends install nodejs \ + && echo "node version: $(node --version)" \ + && echo "npm version: $(npm --version)" + +# Copy SDK code into the container +RUN mkdir -p $HOME/js-algorand-sdk +COPY . $HOME/js-algorand-sdk +WORKDIR $HOME/js-algorand-sdk + +ARG TEST_BROWSER +ENV TEST_BROWSER=$TEST_BROWSER + +ARG CI +ENV CI=$CI + +RUN npm ci +RUN npm run prepare-browser-tests + +# Run integration tests +CMD ["/bin/bash", "-c", "make unit && make integration && make smoke-test-examples"] diff --git a/tests/cucumber/integration.tags b/tests/cucumber/integration.tags new file mode 100644 index 0000000..32a59c3 --- /dev/null +++ b/tests/cucumber/integration.tags @@ -0,0 +1,18 @@ +@abi +@algod +@applications.boxes +@applications.verified +@assets +@auction +@c2c +@compile +@compile.disassemble +@compile.sourcemap +@dryrun +@kmd +@rekey_v1 +@send +@send.keyregtxn +@simulate +@simulate.lift_log_limits +@simulate.extra_opcode_budget diff --git a/tests/cucumber/steps/index.js b/tests/cucumber/steps/index.js new file mode 100644 index 0000000..abb0733 --- /dev/null +++ b/tests/cucumber/steps/index.js @@ -0,0 +1,584 @@ +/* eslint-disable no-console,global-require,no-loop-func,func-names */ +const assert = require('assert'); +const { Buffer } = require('buffer'); +const path = require('path'); +const fs = require('fs'); +const { + BeforeAll, + After, + AfterAll, + Given, + When, + Then, + setDefaultTimeout, +} = require('cucumber'); +const express = require('express'); +const ServerMock = require('mock-http-server'); +const getSteps = require('./steps'); + +const cucumberPath = path.dirname(__dirname); +const browser = process.env.TEST_BROWSER; + +console.log('TEST_BROWSER is', browser); + +let browserHeaders = []; +let driver; +let driverBuilder; +if (browser) { + const webdriver = require('selenium-webdriver'); + const chrome = require('selenium-webdriver/chrome'); + const firefox = require('selenium-webdriver/firefox'); + + let chromeOptions = new chrome.Options(); + let firefoxOptions = new firefox.Options(); + + if (process.env.CI) { + chromeOptions = chromeOptions.addArguments( + 'no-sandbox', + 'disable-gpu', + 'headless' + ); + firefoxOptions = firefoxOptions + .setPreference('remote.active-protocols', 1) // Sets protocol to only WebDriver BiDi + .setPreference('network.http.network-changed.timeout', 30) // In Docker tests this apparently matters + .headless(); + } + + driverBuilder = new webdriver.Builder() + .setChromeOptions(chromeOptions) + .setFirefoxOptions(firefoxOptions) + .forBrowser(browser); + + if (process.env.SELENIUM_SERVER_URL) { + driverBuilder = driverBuilder.usingServer(process.env.SELENIUM_SERVER_URL); + } else if (browser === 'chrome') { + require('chromedriver'); + browserHeaders = [ + 'origin', + 'referer', + 'sec-ch-ua', + 'sec-ch-ua-mobile', + 'sec-ch-ua-platform', + 'sec-fetch-dest', + 'sec-fetch-mode', + 'sec-fetch-site', + ]; + } else if (browser === 'firefox') { + require('geckodriver'); + browserHeaders = [ + 'accept-language', + 'origin', + 'referer', + 'sec-fetch-dest', + 'sec-fetch-mode', + 'sec-fetch-site', + ]; + } + + console.log('Testing in browser'); +} else { + console.log('Testing in node'); +} + +let algodMockServerResponder; +let indexerMockServerResponder; +let algodMockServerPathRecorder; +let indexerMockServerPathRecorder; + +const stepOptions = { + algod_token: + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + kmd_token: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + mockAlgodResponderPort: 31337, + mockAlgodResponderHost: 'localhost', + mockIndexerResponderPort: 31338, + mockIndexerResponderHost: 'localhost', + mockAlgodPathRecorderPort: 31339, + mockAlgodPathRecorderHost: 'localhost', + mockIndexerPathRecorderPort: 31340, + mockIndexerPathRecorderHost: 'localhost', +}; + +async function createIndexerMockServers() { + indexerMockServerResponder = new ServerMock({ + host: stepOptions.mockIndexerResponderHost, + port: stepOptions.mockIndexerResponderPort, + }); + await new Promise((resolve) => { + indexerMockServerResponder.start(resolve); + }); + + indexerMockServerPathRecorder = new ServerMock({ + host: stepOptions.mockIndexerPathRecorderHost, + port: stepOptions.mockIndexerPathRecorderPort, + }); + await new Promise((resolve) => { + indexerMockServerPathRecorder.start(resolve); + }); +} + +async function createAlgodV2MockServers() { + algodMockServerResponder = new ServerMock({ + host: stepOptions.mockAlgodResponderHost, + port: stepOptions.mockAlgodResponderPort, + }); + await new Promise((resolve) => { + algodMockServerResponder.start(resolve); + }); + + algodMockServerPathRecorder = new ServerMock({ + host: stepOptions.mockAlgodPathRecorderHost, + port: stepOptions.mockAlgodPathRecorderPort, + }); + await new Promise((resolve) => { + algodMockServerPathRecorder.start(resolve); + }); +} + +function resetIndexerMockServers() { + if (indexerMockServerPathRecorder !== undefined) { + indexerMockServerPathRecorder.reset(); + } + if (indexerMockServerResponder !== undefined) { + indexerMockServerResponder.reset(); + } +} + +async function resetAlgodV2MockServers() { + if (algodMockServerPathRecorder !== undefined) { + algodMockServerPathRecorder.reset(); + } + if (algodMockServerResponder !== undefined) { + algodMockServerResponder.reset(); + } +} + +async function cleanupIndexerMockServers() { + if (indexerMockServerPathRecorder !== undefined) { + await new Promise((resolve) => { + indexerMockServerPathRecorder.stop(resolve); + }); + } + if (indexerMockServerResponder !== undefined) { + await new Promise((resolve) => { + indexerMockServerResponder.stop(resolve); + }); + } +} + +async function cleanupAlgodV2MockServers() { + if (algodMockServerPathRecorder !== undefined) { + await new Promise((resolve) => { + algodMockServerPathRecorder.stop(resolve); + }); + } + if (algodMockServerResponder !== undefined) { + await new Promise((resolve) => { + algodMockServerResponder.stop(resolve); + }); + } +} + +const browserServerPort = 8080; +let browserServer; + +async function startBrowserServer() { + const app = express(); + app.use(express.static(cucumberPath)); + await new Promise((resolve) => { + browserServer = app.listen(browserServerPort, undefined, resolve); + }); +} + +function stopBrowserServer() { + if (browserServer) { + browserServer.close(); + } +} + +setDefaultTimeout(60000); + +BeforeAll(async () => { + // You can use this hook to write code that will run one time before all scenarios, + // before even the Background steps + await createIndexerMockServers(); + await createAlgodV2MockServers(); + + if (browser) { + await startBrowserServer(); + + driver = await driverBuilder.build(); + + await driver.get( + `http://localhost:${browserServerPort}/browser/index.html` + ); + + const title = await driver.getTitle(); + + if (title !== 'Algosdk Browser Testing') { + throw new Error(`Incorrect title: ${title}`); + } + + const options = { ignoreReturn: true, ...stepOptions }; + + // populate steps in browser context + await driver.executeScript(getSteps, options); + } +}); + +After(async () => { + // this code is run after each individual scenario + resetIndexerMockServers(); + resetAlgodV2MockServers(); +}); + +AfterAll(async () => { + // this cleanup code is run after all scenarios are done + + if (browser) { + await driver.quit(); + stopBrowserServer(); + } + + await cleanupIndexerMockServers(); + await cleanupAlgodV2MockServers(); +}); + +const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'OPTIONS, POST, GET, DELETE', + 'Access-Control-Allow-Headers': + 'X-Algo-API-Token, X-Indexer-API-Token, Content-Type', + 'Access-Control-Max-Age': 2592000, +}; + +function getUnitTestFileContentsAsString(fileName, directory) { + return fs + .readFileSync( + path.join(cucumberPath, 'features', 'unit', directory, fileName) + ) + .toString(); +} + +function setupMockServerForResponses(fileName, jsonDirectory, mockServer) { + const resultString = fs + .readFileSync( + path.join(cucumberPath, 'features', 'resources', jsonDirectory, fileName) + ) + .toString(); + + let headers = corsHeaders; // example headers: { "content-type": "application/json" } + let body; // example body: JSON.stringify({ hello: "world" } + if (fileName.endsWith('json')) { + headers = { 'content-type': 'application/json', ...corsHeaders }; + body = resultString; + } + if (fileName.endsWith('base64')) { + headers = { 'content-type': 'application/msgpack', ...corsHeaders }; + body = Buffer.from(resultString, 'base64'); + } + let statusCode = 200; + if (fileName.indexOf('Error') > -1) { + statusCode = 500; + } + mockServer.on({ + method: 'OPTIONS', + path: '*', + reply: { + status: 204, + headers: corsHeaders, + }, + }); + mockServer.on({ + method: 'GET', + path: '*', + reply: { + status: statusCode, + headers, + body, + }, + }); + mockServer.on({ + method: 'POST', + path: '*', + reply: { + status: statusCode, + headers, + body, + }, + }); + return body; +} + +function setupMockServerForPaths(mockServer) { + mockServer.on({ + method: 'OPTIONS', + path: '*', + reply: { + headers: corsHeaders, + status: 204, + }, + }); + mockServer.on({ + method: 'GET', + path: '*', + reply: { + headers: corsHeaders, + status: 200, + }, + }); + mockServer.on({ + method: 'POST', + path: '*', + reply: { + headers: corsHeaders, + status: 200, + }, + }); + mockServer.on({ + method: 'DELETE', + path: '*', + reply: { + headers: corsHeaders, + status: 200, + }, + }); +} + +function getMockServerRequestUrlsMethodsHeaders(mockServer) { + return mockServer + .requests() + .filter((req) => req.method !== 'OPTIONS') // ignore cors preflight requests from the browser + .map((req) => ({ method: req.method, url: req.url, headers: req.headers })); +} + +function isFunction(functionToCheck) { + return ( + functionToCheck && {}.toString.call(functionToCheck) === '[object Function]' + ); +} + +const steps = getSteps(stepOptions); + +if (browser) { + for (const type of Object.keys(steps)) { + for (const name of Object.keys(steps[type])) { + const originalFn = steps[type][name]; + + // have to return a promise here instead of making rcpFn async because internally cucumber + // uses bluebird.race to resolve this, and for some reason that leaks rejections when this + // is async ¯\_(ツ)_/¯ + const rpcFn = (...args) => { + const asyncRpcFn = async () => { + let rpcArgs = args; + if (isFunction(rpcArgs[rpcArgs.length - 1])) { + // get rid of callback cucumber provides + rpcArgs = args.slice(0, rpcArgs.length - 1); + } + + for (const arg of rpcArgs) { + assert.deepStrictEqual( + JSON.parse(JSON.stringify(arg)), + arg, + `${JSON.stringify( + arg + )} cannot be deserialized via JSON.parse and will fail browser tests` + ); + if (arg instanceof Uint8Array) { + // cannot send Uint8Array or Buffer objects because the arguments will get JSON + // encoded when transmitted to the browser + throw new Error( + `Attempted to send binary data to the browser when invoking test '${type} ${name}'` + ); + } + } + + const { error } = await driver.executeAsyncScript( + // variables are `scoped` because they exist in the upper scope + async (scopedType, scopedName, ...rest) => { + const done = rest[rest.length - 1]; + try { + const testArgs = rest.slice(0, rest.length - 1); + const test = window.getStep(scopedType, scopedName); // eslint-disable-line no-undef + await test.apply(window.testWorld, testArgs); // eslint-disable-line no-undef + done({ error: null }); + } catch (err) { + console.error(err); + done({ error: `${err.toString()}\n${err.stack}` }); + } + }, + type, + name, + ...rpcArgs + ); + + if (error) { + throw new Error( + `Error from test '${type} ${name}': ${error}\n ^ --- browser ---` + ); + } + }; + + return asyncRpcFn(); + }; + + // Need to make it look like rcpFn takes the same number of args as originalFn for cucumber + Object.defineProperty(rpcFn, 'length', { + value: originalFn.length, + writable: false, + }); + + steps[type][name] = rpcFn.bind(null); + } + } +} + +for (const name of Object.keys(steps.given)) { + const fn = steps.given[name]; + if (name === 'mock http responses in {string} loaded from {string}') { + Given(name, function (fileName, jsonDirectory) { + let body1 = setupMockServerForResponses( + fileName, + jsonDirectory, + algodMockServerResponder + ); + let body2 = setupMockServerForResponses( + fileName, + jsonDirectory, + indexerMockServerResponder + ); + let format = 'json'; + if (fileName.endsWith('base64')) { + format = 'msgp'; + } + if (Buffer.isBuffer(body1)) { + body1 = body1.toString('base64'); + } + if (Buffer.isBuffer(body2)) { + body2 = body2.toString('base64'); + } + return fn.call(this, body2 || body1, format); + }); + } else if ( + name === + 'mock http responses in {string} loaded from {string} with status {int}.' + ) { + Given(name, function (fileName, jsonDirectory, status) { + let body1 = setupMockServerForResponses( + fileName, + jsonDirectory, + algodMockServerResponder + ); + let body2 = setupMockServerForResponses( + fileName, + jsonDirectory, + indexerMockServerResponder + ); + let format = 'json'; + if (fileName.endsWith('base64')) { + format = 'msgp'; + } + if (Buffer.isBuffer(body1)) { + body1 = body1.toString('base64'); + } + if (Buffer.isBuffer(body2)) { + body2 = body2.toString('base64'); + } + return fn.call(this, body2 || body1, status, format); + }); + } else if (name === 'mock server recording request paths') { + Given(name, function () { + setupMockServerForPaths(algodMockServerPathRecorder); + setupMockServerForPaths(indexerMockServerPathRecorder); + return fn.call(this); + }); + } else if (name === 'expected headers') { + Given(name, function (dataTable) { + return fn.call(this, [ + ...dataTable.rows().map((entry) => entry[0]), + ...browserHeaders, + ]); + }); + } else { + Given(name, fn); + } +} +for (const name of Object.keys(steps.when)) { + const fn = steps.when[name]; + When(name, fn); +} +for (const name of Object.keys(steps.then)) { + const fn = steps.then[name]; + if (name === 'expect the path used to be {string}') { + Then(name, function (expectedRequestPath) { + // get all requests the mockservers have seen since reset + const algodSeenRequests = getMockServerRequestUrlsMethodsHeaders( + algodMockServerPathRecorder + ); + const indexerSeenRequests = getMockServerRequestUrlsMethodsHeaders( + indexerMockServerPathRecorder + ); + return fn.call( + this, + algodSeenRequests, + indexerSeenRequests, + expectedRequestPath + ); + }); + } else if (name === 'expect the request to be {string} {string}') { + Then(name, function (expectedRequestType, expectedRequestPath) { + // get all requests the mockservers have seen since reset + const algodSeenRequests = getMockServerRequestUrlsMethodsHeaders( + algodMockServerPathRecorder + ); + const indexerSeenRequests = getMockServerRequestUrlsMethodsHeaders( + indexerMockServerPathRecorder + ); + return fn.call( + this, + algodSeenRequests, + indexerSeenRequests, + expectedRequestType, + expectedRequestPath + ); + }); + } else if (name === 'we expect the path used to be {string}') { + Then(name, function (expectedRequestPath) { + // get all requests the mockservers have seen since reset + const algodSeenRequests = getMockServerRequestUrlsMethodsHeaders( + algodMockServerPathRecorder + ); + const indexerSeenRequests = getMockServerRequestUrlsMethodsHeaders( + indexerMockServerPathRecorder + ); + return fn.call( + this, + algodSeenRequests, + indexerSeenRequests, + expectedRequestPath + ); + }); + } else if ( + name === 'expect the observed header keys to equal the expected header keys' + ) { + Then(name, function () { + // get all requests the mockservers have seen since reset + const algodSeenRequests = getMockServerRequestUrlsMethodsHeaders( + algodMockServerPathRecorder + ); + const indexerSeenRequests = getMockServerRequestUrlsMethodsHeaders( + indexerMockServerPathRecorder + ); + return fn.call(this, algodSeenRequests, indexerSeenRequests); + }); + } else if ( + name === 'the produced json should equal {string} loaded from {string}' + ) { + Then(name, function (fileName, directory) { + const expectedJson = getUnitTestFileContentsAsString(fileName, directory); + return fn.call(this, expectedJson); + }); + } else { + Then(name, fn); + } +} diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js new file mode 100644 index 0000000..6f55423 --- /dev/null +++ b/tests/cucumber/steps/steps.js @@ -0,0 +1,4884 @@ +/* eslint-disable func-names,radix */ +const { Buffer } = require('buffer'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const algosdk = require('../../../src/index'); +const nacl = require('../../../src/nacl/naclWrappers'); + +const maindir = path.dirname(path.dirname(path.dirname(__dirname))); + +function keyPairFromSecretKey(sk) { + return nacl.keyPairFromSecretKey(sk); +} + +function keyPairFromSeed(seed) { + return nacl.keyPairFromSeed(seed); +} + +function genericHash(toHash) { + return nacl.genericHash(toHash); +} + +async function loadResource(res) { + const p = path.join( + maindir, + 'tests', + 'cucumber', + 'features', + 'resources', + res + ); + return new Promise((resolve, reject) => { + fs.readFile(p, (err, content) => { + if (err) { + reject(err); + } else { + resolve(content); + } + }); + }); +} + +// START OBJECT CREATION FUNCTIONS + +/** + * If you wish to compare complex objects from different steps, these functions must be used instead + * of creating the objects directly. This is because of this firefox issue: https://github.com/mozilla/geckodriver/issues/1798 + * + * If you get an assertion error on firefox that says 'Values identical but not reference-equal', + * you should probably use these functions or make new ones as needed. + */ + +function makeUint8Array(arg) { + return new Uint8Array(arg); +} + +function makeABIMethod(arg) { + return new algosdk.ABIMethod(arg); +} + +function makeABIContract(arg) { + return new algosdk.ABIContract(arg); +} + +function makeArray(...args) { + return args; +} + +function makeObject(obj) { + return { ...obj }; +} + +function parseJSON(json) { + return JSON.parse(json); +} + +// END OBJECT CREATION FUNCTIONS + +const steps = { + given: {}, + when: {}, + then: {}, +}; + +/** + * The getSteps function defines the cucumber steps and returns them. + * + * IMPORTANT: This function is made to run in the context of this script in Node and by itself in + * browsers. In order to keep it working in both contexts, make sure each context provides all the + * necessary external functions and variables. That means every time you add import a module or add + * a new helper function or variable above, you should also add an equivalent function or variable + * to tests/cucumber/browser/test.js. + * + * You should also avoid using any Node-specific features in this function. Instead, create a helper + * function like loadResource. In Node it uses fs to load a file, and in the browser it sends an + * HTTP GET request to load a file. + */ +module.exports = function getSteps(options) { + function Given(name, fn) { + if (Object.prototype.hasOwnProperty.call(steps.given, name)) { + throw new Error(`Duplicate step: given ${name}`); + } + steps.given[name] = fn; + } + + function When(name, fn) { + if (Object.prototype.hasOwnProperty.call(steps.when, name)) { + throw new Error(`Duplicate step: when ${name}`); + } + steps.when[name] = fn; + } + + function Then(name, fn) { + if (Object.prototype.hasOwnProperty.call(steps.then, name)) { + throw new Error(`Duplicate step: then ${name}`); + } + steps.then[name] = fn; + } + + // Dev Mode State + const DEV_MODE_INITIAL_MICROALGOS = 100_000_000; + + const { algod_token: algodToken, kmd_token: kmdToken } = options; + + // String parsing helper methods + function processAppArgs(subArg) { + switch (subArg[0]) { + case 'str': + return makeUint8Array(Buffer.from(subArg[1])); + case 'int': + return makeUint8Array(algosdk.encodeUint64(parseInt(subArg[1], 10))); + case 'addr': + return algosdk.decodeAddress(subArg[1]).publicKey; + case 'b64': + return makeUint8Array(Buffer.from(subArg[1], 'base64')); + default: + throw Error(`did not recognize app arg of type ${subArg[0]}`); + } + } + + function splitAndProcessAppArgs(inArgs) { + if (inArgs == null || inArgs === '') { + return []; + } + const splitArgs = inArgs.split(','); + const subArgs = []; + splitArgs.forEach((subArg) => { + subArgs.push(subArg.split(':')); + }); + const appArgs = makeArray(); + subArgs.forEach((subArg) => { + appArgs.push(processAppArgs(subArg)); + }); + return appArgs; + } + + function splitAndProcessBoxReferences(boxRefs) { + if (boxRefs == null || boxRefs === '') { + return makeArray(); + } + const splitRefs = boxRefs.split(','); + const boxRefArray = makeArray(); + let appIndex = 0; + + for (let i = 0; i < splitRefs.length; i++) { + if (i % 2 === 0) { + appIndex = parseInt(splitRefs[i]); + } else { + const refArg = splitRefs[i].split(':'); + boxRefArray.push({ + appIndex, + name: processAppArgs(refArg), + }); + } + } + return boxRefArray; + } + + Given('a kmd client', function () { + this.kcl = new algosdk.Kmd(kmdToken, 'http://localhost', 60001); + return this.kcl; + }); + + Given('an algod v2 client', function () { + this.v2Client = new algosdk.Algodv2(algodToken, 'http://localhost', 60000); + }); + + Given('an indexer v2 client', function () { + this.indexerV2client = new algosdk.Indexer('', 'http://localhost', 59999); + }); + + Given('wallet information', async function () { + this.wallet_name = 'unencrypted-default-wallet'; + this.wallet_pswd = ''; + + const result = await this.kcl.listWallets(); + for (let i = 0; i < result.wallets.length; i++) { + const w = result.wallets[i]; + if (w.name === this.wallet_name) { + this.wallet_id = w.id; + break; + } + } + this.handle = await this.kcl.initWalletHandle( + this.wallet_id, + this.wallet_pswd + ); + this.handle = this.handle.wallet_handle_token; + this.accounts = await this.kcl.listKeys(this.handle); + this.accounts = this.accounts.addresses; + return this.accounts; + }); + + When('I get versions with algod', async function () { + this.versions = await this.v2Client.versionsCheck().do(); + this.versions = this.versions.versions; + return this.versions; + }); + + Then('v1 should be in the versions', function () { + assert.deepStrictEqual(true, this.versions.indexOf('v1') >= 0); + }); + + Then('v2 should be in the versions', function () { + assert.deepStrictEqual(true, this.versions.indexOf('v2') >= 0); + }); + + When('I get versions with kmd', async function () { + this.versions = await this.kcl.versions(); + this.versions = this.versions.versions; + return this.versions; + }); + + Given( + 'payment transaction parameters {int} {int} {int} {string} {string} {string} {int} {string} {string}', + function (fee, fv, lv, gh, to, close, amt, gen, note) { + this.fee = parseInt(fee); + this.fv = parseInt(fv); + this.lv = parseInt(lv); + this.gh = gh; + this.to = to; + if (close !== 'none') { + this.close = close; + } + this.amt = parseInt(amt); + if (gen !== 'none') { + this.gen = gen; + } + if (note !== 'none') { + this.note = makeUint8Array(Buffer.from(note, 'base64')); + } + } + ); + + Given('mnemonic for private key {string}', function (mn) { + const result = algosdk.mnemonicToSecretKey(mn); + this.pk = result.addr; + + this.sk = result.sk; + }); + + Given('multisig addresses {string}', function (addresses) { + const addrlist = addresses.split(' '); + this.msig = { + version: 1, + threshold: 2, + addrs: addrlist, + }; + this.pk = algosdk.multisigAddress(this.msig); + }); + + When('I sign the transaction with the private key', function () { + const obj = algosdk.signTransaction(this.txn, this.sk); + this.stx = obj.blob; + }); + + When('I sign the multisig transaction with the private key', function () { + const obj = algosdk.signMultisigTransaction(this.txn, this.msig, this.sk); + this.stx = obj.blob; + }); + + When('I sign the transaction with kmd', async function () { + this.stxKmd = await this.kcl.signTransaction( + this.handle, + this.wallet_pswd, + this.txn + ); + return this.stxKmd; + }); + + When('I sign the multisig transaction with kmd', async function () { + const addrs = []; + for (let i = 0; i < this.msig.addrs.length; i++) { + addrs.push( + Buffer.from( + algosdk.decodeAddress(this.msig.addrs[i]).publicKey + ).toString('base64') + ); + } + await this.kcl.importMultisig( + this.handle, + this.msig.version, + this.msig.threshold, + addrs + ); + + const key = algosdk.decodeAddress(this.pk).publicKey; + this.stxKmd = await this.kcl.signMultisigTransaction( + this.handle, + this.wallet_pswd, + this.txn, + key, + null + ); + this.stxKmd = this.stxKmd.multisig; + return this.stxKmd; + }); + + Then( + 'the signed transaction should equal the golden {string}', + function (golden) { + assert.deepStrictEqual( + Buffer.from(golden, 'base64'), + Buffer.from(this.stx) + ); + } + ); + + Then( + 'the signed transaction should equal the kmd signed transaction', + function () { + assert.deepStrictEqual(Buffer.from(this.stx), Buffer.from(this.stxKmd)); + } + ); + + Then( + 'the multisig address should equal the golden {string}', + function (golden) { + assert.deepStrictEqual(algosdk.multisigAddress(this.msig), golden); + } + ); + + Then( + 'the multisig transaction should equal the golden {string}', + function (golden) { + assert.deepStrictEqual( + Buffer.from(golden, 'base64'), + Buffer.from(this.stx) + ); + } + ); + + Then( + 'the multisig transaction should equal the kmd signed multisig transaction', + async function () { + await this.kcl.deleteMultisig( + this.handle, + this.wallet_pswd, + algosdk.multisigAddress(this.msig) + ); + const s = algosdk.decodeObj(this.stx); + const m = algosdk.encodeObj(s.msig); + assert.deepStrictEqual( + Buffer.from(m), + Buffer.from(this.stxKmd, 'base64') + ); + } + ); + + When('I generate a key using kmd', async function () { + this.pk = await this.kcl.generateKey(this.handle); + this.pk = this.pk.address; + return this.pk; + }); + + When( + 'I generate a key using kmd for rekeying and fund it', + async function () { + this.rekey = await this.kcl.generateKey(this.handle); + this.rekey = this.rekey.address; + // Fund the rekey address with some Algos + const sp = await this.v2Client.getTransactionParams().do(); + if (sp.firstRound === 0) sp.firstRound = 1; + const fundingTxnArgs = { + from: this.accounts[0], + to: this.rekey, + amount: DEV_MODE_INITIAL_MICROALGOS, + fee: sp.fee, + firstRound: sp.firstRound, + lastRound: sp.lastRound, + genesisHash: sp.genesisHash, + genesisID: sp.genesisID, + }; + + const stxKmd = await this.kcl.signTransaction( + this.handle, + this.wallet_pswd, + fundingTxnArgs + ); + await this.v2Client.sendRawTransaction(stxKmd).do(); + return this.rekey; + } + ); + + Then('the key should be in the wallet', async function () { + let keys = await this.kcl.listKeys(this.handle); + keys = keys.addresses; + assert.deepStrictEqual(true, keys.indexOf(this.pk) >= 0); + return keys; + }); + + When('I delete the key', async function () { + return this.kcl.deleteKey(this.handle, this.wallet_pswd, this.pk); + }); + + Then('the key should not be in the wallet', async function () { + let keys = await this.kcl.listKeys(this.handle); + keys = keys.addresses; + assert.deepStrictEqual(false, keys.indexOf(this.pk) >= 0); + return keys; + }); + + When('I generate a key', function () { + const result = algosdk.generateAccount(); + this.pk = result.addr; + this.sk = result.sk; + }); + + When('I import the key', async function () { + return this.kcl.importKey(this.handle, this.sk); + }); + + Then( + 'the private key should be equal to the exported private key', + async function () { + let exp = await this.kcl.exportKey( + this.handle, + this.wallet_pswd, + this.pk + ); + exp = exp.private_key; + assert.deepStrictEqual( + Buffer.from(exp).toString('base64'), + Buffer.from(this.sk).toString('base64') + ); + return this.kcl.deleteKey(this.handle, this.wallet_pswd, this.pk); + } + ); + + When('I get the private key', async function () { + const sk = await this.kcl.exportKey(this.handle, this.wallet_pswd, this.pk); + this.sk = sk.private_key; + return this.sk; + }); + + Given( + 'default transaction with parameters {int} {string}', + async function (amt, note) { + [this.pk] = this.accounts; + const result = await this.v2Client.getTransactionParams().do(); + this.txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: this.accounts[0], + to: this.accounts[1], + amount: parseInt(amt), + suggestedParams: result, + note: makeUint8Array(Buffer.from(note, 'base64')), + }); + return this.txn; + } + ); + + Given( + 'default transaction with parameters {int} {string} and rekeying key', + async function (amt, note) { + this.pk = this.rekey; + const result = await this.v2Client.getTransactionParams().do(); + this.lastRound = result.lastRound; + this.txn = { + from: this.rekey, + to: this.accounts[1], + fee: result.fee, + firstRound: result.firstRound, + lastRound: result.lastRound, + genesisHash: result.genesisHash, + genesisID: result.genesisID, + note: makeUint8Array(Buffer.from(note, 'base64')), + amount: parseInt(amt), + }; + return this.txn; + } + ); + + Given( + 'default multisig transaction with parameters {int} {string}', + async function (amt, note) { + [this.pk] = this.accounts; + const result = await this.v2Client.getTransactionParams().do(); + this.msig = { + version: 1, + threshold: 1, + addrs: this.accounts, + }; + + this.txn = { + from: algosdk.multisigAddress(this.msig), + to: this.accounts[1], + fee: result.fee, + firstRound: result.firstRound, + lastRound: result.lastRound, + genesisHash: result.genesisHash, + genesisID: result.genesisID, + note: makeUint8Array(Buffer.from(note, 'base64')), + amount: parseInt(amt), + }; + return this.txn; + } + ); + + When('I import the multisig', async function () { + const addrs = []; + for (let i = 0; i < this.msig.addrs.length; i++) { + addrs.push( + Buffer.from( + algosdk.decodeAddress(this.msig.addrs[i]).publicKey + ).toString('base64') + ); + } + return this.kcl.importMultisig( + this.handle, + this.msig.version, + this.msig.threshold, + addrs + ); + }); + + Then('the multisig should be in the wallet', async function () { + let keys = await this.kcl.listMultisig(this.handle); + keys = keys.addresses; + assert.deepStrictEqual( + true, + keys.indexOf(algosdk.multisigAddress(this.msig)) >= 0 + ); + return keys; + }); + + Then('the multisig should not be in the wallet', async function () { + let keys = await this.kcl.listMultisig(this.handle); + if (typeof keys.addresses === 'undefined') { + return true; + } + + keys = keys.addresses; + assert.deepStrictEqual( + false, + keys.indexOf(algosdk.multisigAddress(this.msig)) >= 0 + ); + return keys; + }); + + When('I export the multisig', async function () { + this.msigExp = await this.kcl.exportMultisig( + this.handle, + algosdk.multisigAddress(this.msig) + ); + return this.msigExp; + }); + + When('I delete the multisig', async function () { + return this.kcl.deleteMultisig( + this.handle, + this.wallet_pswd, + algosdk.multisigAddress(this.msig) + ); + }); + + Then('the multisig should equal the exported multisig', function () { + for (let i = 0; i < this.msigExp.length; i++) { + assert.deepStrictEqual( + algosdk.encodeAddress(Buffer.from(this.msigExp[i], 'base64')), + this.msig.addrs[i] + ); + } + }); + + Then('the node should be healthy', async function () { + const health = await this.v2Client.healthCheck().do(); + assert.deepStrictEqual(health, makeObject({})); + }); + + Then('I get the ledger supply', async function () { + return this.v2Client.supply().do(); + }); + + When('I create a bid', function () { + let addr = algosdk.generateAccount(); + this.sk = addr.sk; + addr = addr.addr; + this.bid = { + bidderKey: addr, + bidAmount: 1, + maxPrice: 2, + bidID: 3, + auctionKey: addr, + auctionID: 4, + }; + return this.bid; + }); + + When('I encode and decode the bid', function () { + this.sbid = algosdk.decodeObj(algosdk.encodeObj(this.sbid)); + return this.sbid; + }); + + When('I sign the bid', function () { + this.sbid = algosdk.decodeObj(algosdk.signBid(this.bid, this.sk)); + this.oldBid = algosdk.decodeObj(algosdk.signBid(this.bid, this.sk)); + }); + + Then('the bid should still be the same', function () { + assert.deepStrictEqual( + algosdk.encodeObj(this.sbid), + algosdk.encodeObj(this.oldBid) + ); + }); + + When('I decode the address', function () { + this.old = this.pk; + this.addrBytes = algosdk.decodeAddress(this.pk).publicKey; + }); + + When('I encode the address', function () { + this.pk = algosdk.encodeAddress(this.addrBytes); + }); + + Then('the address should still be the same', function () { + assert.deepStrictEqual(this.pk, this.old); + }); + + When('I convert the private key back to a mnemonic', function () { + this.mn = algosdk.secretKeyToMnemonic(this.sk); + }); + + Then('the mnemonic should still be the same as {string}', function (mn) { + assert.deepStrictEqual(this.mn, mn); + }); + + Given('mnemonic for master derivation key {string}', function (mn) { + this.mdk = algosdk.mnemonicToMasterDerivationKey(mn); + }); + + When('I convert the master derivation key back to a mnemonic', function () { + this.mn = algosdk.masterDerivationKeyToMnemonic(this.mdk); + }); + + When('I create the flat fee payment transaction', function () { + this.txn = { + to: this.to, + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + genesisHash: this.gh, + flatFee: true, + }; + if (this.gen) { + this.txn.genesisID = this.gen; + } + if (this.close) { + this.txn.closeRemainderTo = this.close; + } + if (this.note) { + this.txn.note = this.note; + } + if (this.amt) { + this.txn.amount = this.amt; + } + }); + + Given('encoded multisig transaction {string}', function (encTxn) { + this.mtx = Buffer.from(encTxn, 'base64'); + this.stx = algosdk.decodeObj(this.mtx); + }); + + When('I append a signature to the multisig transaction', function () { + const addresses = this.stx.msig.subsig.slice(); + for (let i = 0; i < addresses.length; i++) { + addresses[i] = algosdk.encodeAddress(addresses[i].pk); + } + const msig = { + version: this.stx.msig.v, + threshold: this.stx.msig.thr, + addrs: addresses, + }; + this.stx = algosdk.appendSignMultisigTransaction( + this.mtx, + msig, + this.sk + ).blob; + }); + + When('I merge the multisig transactions', function () { + this.stx = algosdk.mergeMultisigTransactions(this.mtxs); + }); + + When('I convert {int} microalgos to algos and back', function (microalgos) { + this.microalgos = algosdk + .algosToMicroalgos(algosdk.microalgosToAlgos(microalgos)) + .toString(); + }); + + Then( + 'it should still be the same amount of microalgos {int}', + function (microalgos) { + assert.deepStrictEqual(this.microalgos, microalgos.toString()); + } + ); + + Given('encoded multisig transactions {string}', function (encTxns) { + this.mtxs = []; + const mtxs = encTxns.split(' '); + for (let i = 0; i < mtxs.length; i++) { + this.mtxs.push(Buffer.from(mtxs[i], 'base64')); + } + }); + + When('I create the multisig payment transaction', function () { + this.txn = { + from: algosdk.multisigAddress(this.msig), + to: this.to, + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + genesisHash: this.gh, + }; + if (this.gen) { + this.txn.genesisID = this.gen; + } + if (this.close) { + this.txn.closeRemainderTo = this.close; + } + if (this.note) { + this.txn.note = this.note; + } + if (this.amt) { + this.txn.amount = this.amt; + } + return this.txn; + }); + + When('I create the multisig payment transaction with zero fee', function () { + this.txn = { + from: algosdk.multisigAddress(this.msig), + to: this.to, + fee: this.fee, + flatFee: true, + firstRound: this.fv, + lastRound: this.lv, + genesisHash: this.gh, + }; + if (this.gen) { + this.txn.genesisID = this.gen; + } + if (this.close) { + this.txn.closeRemainderTo = this.close; + } + if (this.note) { + this.txn.note = this.note; + } + if (this.amt) { + this.txn.amount = this.amt; + } + return this.txn; + }); + + When('I send the transaction', async function () { + const txid = await this.v2Client.sendRawTransaction(this.stx).do(); + this.txid = txid.txId; + this.appTxid = txid; // Alias to use in waitForTransaction. + return this.txid; + }); + + When('I send the kmd-signed transaction', async function () { + const txid = await this.v2Client.sendRawTransaction(this.stxKmd).do(); + this.txid = txid.txId; + this.appTxid = txid; // Alias to use in waitForTransaction. + return this.txid; + }); + + // eslint-disable-next-line consistent-return + When('I send the multisig transaction', async function () { + try { + this.txid = await this.v2Client.sendRawTransaction(this.stx).do(); + this.err = false; + return this.txid; + } catch (e) { + this.err = true; + } + }); + + Then('the transaction should not go through', function () { + assert.deepStrictEqual(true, this.err); + }); + + When('I create a wallet', async function () { + this.wallet_name = 'Walletjs'; + this.wallet_pswd = ''; + this.wallet_id = await this.kcl.createWallet( + this.wallet_name, + this.wallet_pswd + ); + this.wallet_id = this.wallet_id.wallet.id; + return this.wallet_id; + }); + + Then('the wallet should exist', async function () { + const result = await this.kcl.listWallets(); + let exists = false; + for (let i = 0; i < result.wallets.length; i++) { + const w = result.wallets[i]; + if (w.name === this.wallet_name) { + exists = true; + } + } + assert.deepStrictEqual(true, exists); + }); + + When('I get the wallet handle', async function () { + this.handle = await this.kcl.initWalletHandle( + this.wallet_id, + this.wallet_pswd + ); + this.handle = this.handle.wallet_handle_token; + return this.handle; + }); + + Then('I can get the master derivation key', async function () { + const mdk = await this.kcl.exportMasterDerivationKey( + this.handle, + this.wallet_pswd + ); + return mdk; + }); + + When('I rename the wallet', async function () { + this.wallet_name = 'Walletjs_new'; + return this.kcl.renameWallet( + this.wallet_id, + this.wallet_pswd, + this.wallet_name + ); + }); + + Then( + 'I can still get the wallet information with the same handle', + async function () { + return this.kcl.getWallet(this.handle); + } + ); + + When('I renew the wallet handle', async function () { + return this.kcl.renewWalletHandle(this.handle); + }); + + When('I release the wallet handle', async function () { + return this.kcl.releaseWalletHandle(this.handle); + }); + + Then('the wallet handle should not work', async function () { + try { + await this.kcl.renewWalletHandle(this.handle); + this.err = false; + } catch (e) { + this.err = true; + } + assert.deepStrictEqual(true, this.err); + }); + + // When("I read a transaction {string} from file {string}", function(string, num){ + // this.num = num + // this.txn = algosdk.decodeObj( + // makeUint8Array(fs.readFileSync(maindir + '/temp/raw' + num + '.tx')) + // ); + // return this.txn + // }); + + // When("I write the transaction to file", function(){ + // fs.writeFileSync( + // maindir + '/temp/raw' + this.num + '.tx', + // Buffer.from(algosdk.encodeObj(this.txn)) + // ); + // }); + + // Then("the transaction should still be the same", function(){ + // stxnew = makeUint8Array(fs.readFileSync(maindir + '/temp/raw' + this.num + '.tx')); + // stxold = makeUint8Array(fs.readFileSync(maindir + '/temp/old' + this.num + '.tx')); + // assert.deepStrictEqual(stxnew, stxold); + // }); + + // Then("I do my part", async function(){ + // stx = makeUint8Array(fs.readFileSync(maindir + '/temp/txn.tx')); + // this.txid = await this.acl.sendRawTransaction(stx) + // this.txid = this.txid.txId + // return this.txid + // }) + + Then('I can get account information', async function () { + await this.v2Client.accountInformation(this.pk).do(); + return this.kcl.deleteKey(this.handle, this.wallet_pswd, this.pk); + }); + + Given( + 'default V2 key registration transaction {string}', + async function (type) { + const voteKey = makeUint8Array( + Buffer.from('9mr13Ri8rFepxN3ghIUrZNui6LqqM5hEzB45Rri5lkU=', 'base64') + ); + const selectionKey = makeUint8Array( + Buffer.from('dx717L3uOIIb/jr9OIyls1l5Ei00NFgRa380w7TnPr4=', 'base64') + ); + const stateProofKey = makeUint8Array( + Buffer.from( + 'mYR0GVEObMTSNdsKM6RwYywHYPqVDqg3E4JFzxZOreH9NU8B+tKzUanyY8AQ144hETgSMX7fXWwjBdHz6AWk9w==', + 'base64' + ) + ); + + const from = this.accounts[0]; + this.pk = from; + + const result = await this.v2Client.getTransactionParams().do(); + const suggestedParams = { + fee: result.fee, + firstRound: result.firstRound, + lastRound: result.lastRound, + genesisHash: result.genesisHash, + genesisID: result.genesisID, + }; + this.lastRound = result.lastRound; + + if (type === 'online') { + this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ + from, + voteKey, + selectionKey, + stateProofKey, + voteFirst: 1, + voteLast: 2000, + voteKeyDilution: 10, + suggestedParams, + }); + } else if (type === 'offline') { + this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ + from, + suggestedParams, + }); + } else if (type === 'nonparticipation') { + this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ + from, + nonParticipation: true, + suggestedParams, + }); + } else { + throw new Error(`Unrecognized keyreg type: ${type}`); + } + } + ); + + /// ///////////////////////////////// + // begin asset tests + /// ///////////////////////////////// + + Given('asset test fixture', function () { + this.assetTestFixture = { + creator: '', + index: 0, + name: 'testcoin', + unitname: 'coins', + url: 'http://test', + metadataHash: 'fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh', + expectedParams: undefined, + queriedParams: undefined, + lastTxn: undefined, + }; + }); + + Given( + 'default asset creation transaction with total issuance {int}', + async function (issuance) { + [this.assetTestFixture.creator] = this.accounts; + this.params = await this.v2Client.getTransactionParams().do(); + this.fee = this.params.fee; + this.fv = this.params.firstRound; + this.lv = this.params.lastRound; + this.note = undefined; + this.gh = this.params.genesisHash; + const parsedIssuance = parseInt(issuance); + const decimals = 0; + const defaultFrozen = false; + const assetName = this.assetTestFixture.name; + const unitName = this.assetTestFixture.unitname; + const assetURL = this.assetTestFixture.url; + const { metadataHash } = this.assetTestFixture; + const manager = this.assetTestFixture.creator; + const reserve = this.assetTestFixture.creator; + const freeze = this.assetTestFixture.creator; + const clawback = this.assetTestFixture.creator; + const genesisID = ''; + const type = 'acfg'; + + this.assetTestFixture.lastTxn = { + from: this.assetTestFixture.creator, + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + note: this.note, + genesisHash: this.gh, + assetTotal: parsedIssuance, + assetDecimals: decimals, + assetDefaultFrozen: defaultFrozen, + assetUnitName: unitName, + assetName, + assetURL, + assetMetadataHash: metadataHash, + assetManager: manager, + assetReserve: reserve, + assetFreeze: freeze, + assetClawback: clawback, + genesisID, + type, + }; + // update vars used by other helpers + this.assetTestFixture.expectedParams = { + creator: this.assetTestFixture.creator, + total: parsedIssuance, + defaultfrozen: defaultFrozen, + unitname: unitName, + assetname: assetName, + url: assetURL, + metadatahash: Buffer.from(metadataHash).toString('base64'), + managerkey: manager, + reserveaddr: reserve, + freezeaddr: freeze, + clawbackaddr: clawback, + decimals, + }; + this.txn = this.assetTestFixture.lastTxn; + this.lastRound = this.params.lastRound; + [this.pk] = this.accounts; + } + ); + + Given( + 'default-frozen asset creation transaction with total issuance {int}', + async function (issuance) { + [this.assetTestFixture.creator] = this.accounts; + this.params = await this.v2Client.getTransactionParams().do(); + this.fee = this.params.fee; + this.fv = this.params.firstRound; + this.lv = this.params.lastRound; + this.note = undefined; + this.gh = this.params.genesisHash; + const parsedIssuance = parseInt(issuance); + const decimals = 0; + const defaultFrozen = true; + const assetName = this.assetTestFixture.name; + const unitName = this.assetTestFixture.unitname; + const assetURL = this.assetTestFixture.url; + const { metadataHash } = this.assetTestFixture; + const manager = this.assetTestFixture.creator; + const reserve = this.assetTestFixture.creator; + const freeze = this.assetTestFixture.creator; + const clawback = this.assetTestFixture.creator; + const genesisID = ''; + const type = 'acfg'; + + this.assetTestFixture.lastTxn = { + from: this.assetTestFixture.creator, + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + note: this.note, + genesisHash: this.gh, + assetTotal: parsedIssuance, + assetDecimals: decimals, + assetDefaultFrozen: defaultFrozen, + assetUnitName: unitName, + assetName, + assetURL, + assetMetadataHash: metadataHash, + assetManager: manager, + assetReserve: reserve, + assetFreeze: freeze, + assetClawback: clawback, + genesisID, + type, + }; + // update vars used by other helpers + this.assetTestFixture.expectedParams = { + creator: this.assetTestFixture.creator, + total: parsedIssuance, + defaultfrozen: defaultFrozen, + unitname: unitName, + assetname: assetName, + url: assetURL, + metadatahash: Buffer.from(metadataHash).toString('base64'), + managerkey: manager, + reserveaddr: reserve, + freezeaddr: freeze, + clawbackaddr: clawback, + decimals, + }; + this.txn = this.assetTestFixture.lastTxn; + this.lastRound = this.params.lastRound; + [this.pk] = this.accounts; + } + ); + + // a lambda "return a-b" would suffice for keys.sort, below, + // but define it separately for readability + function sortKeysAscending(a, b) { + if (a > b) { + return 1; + } + if (b > a) { + return -1; + } + return 0; + } + + When('I update the asset index', async function () { + const accountResponse = await this.v2Client + .accountInformation(this.assetTestFixture.creator) + .do(); + const heldAssets = accountResponse['created-assets']; + let assetIds = heldAssets.map((asset) => asset.index); + assetIds = assetIds.sort(sortKeysAscending); + const assetIndex = assetIds[assetIds.length - 1]; + + // this is stored as a string so it can be used as a key later. + this.assetTestFixture.index = assetIndex.toString(); + }); + + When('I get the asset info', async function () { + this.assetTestFixture.queriedParams = await this.v2Client + .getAssetByID(this.assetTestFixture.index) + .do(); + }); + + Then('the asset info should match the expected asset info', function () { + Object.keys(this.assetTestFixture.expectedParams).forEach((key) => { + assert.strictEqual( + true, + this.assetTestFixture.expectedParams[key] === + this.assetTestFixture.queriedParams.params[key] || + typeof this.assetTestFixture.expectedParams[key] === 'undefined' || + typeof this.assetTestFixture.queriedParams.params[key] === 'undefined' + ); + }); + }); + + When( + 'I create a no-managers asset reconfigure transaction', + async function () { + [this.assetTestFixture.creator] = this.accounts; + this.params = await this.v2Client.getTransactionParams().do(); + this.fee = this.params.fee; + this.fv = this.params.firstRound; + this.lv = this.params.lastRound; + this.note = undefined; + this.gh = this.params.genesisHash; + // if we truly supplied no managers at all, it would be an asset destroy txn + // so leave one key written + const manager = this.assetTestFixture.creator; + let reserve; + let freeze; + let clawback; + const genesisID = ''; + const type = 'acfg'; + + this.assetTestFixture.lastTxn = { + from: this.assetTestFixture.creator, + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + note: this.note, + genesisHash: this.gh, + assetManager: manager, + assetReserve: reserve, + assetFreeze: freeze, + assetClawback: clawback, + assetIndex: parseInt(this.assetTestFixture.index), + genesisID, + type, + }; + // update vars used by other helpers + this.assetTestFixture.expectedParams.reserveaddr = ''; + this.assetTestFixture.expectedParams.freezeaddr = ''; + this.assetTestFixture.expectedParams.clawbackaddr = ''; + this.txn = this.assetTestFixture.lastTxn; + this.lastRound = this.params.lastRound; + [this.pk] = this.accounts; + } + ); + + When('I create an asset destroy transaction', async function () { + [this.assetTestFixture.creator] = this.accounts; + this.params = await this.v2Client.getTransactionParams().do(); + this.fee = this.params.fee; + this.fv = this.params.firstRound; + this.lv = this.params.lastRound; + this.note = undefined; + this.gh = this.params.genesisHash; + const genesisID = ''; + const type = 'acfg'; + + this.assetTestFixture.lastTxn = { + from: this.assetTestFixture.creator, + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + note: this.note, + genesisHash: this.gh, + assetIndex: parseInt(this.assetTestFixture.index), + genesisID, + type, + }; + // update vars used by other helpers + this.txn = this.assetTestFixture.lastTxn; + this.lastRound = this.params.lastRound; + [this.pk] = this.accounts; + }); + + Then('I should be unable to get the asset info', async function () { + let failed = false; + try { + await this.v2Client.getAssetByID(this.assetTestFixture.index).do(); + } catch (e) { + failed = true; + } + assert.deepStrictEqual(failed, true); + }); + + When( + 'I create a transaction for a second account, signalling asset acceptance', + async function () { + const accountToUse = this.accounts[1]; + this.params = await this.v2Client.getTransactionParams().do(); + this.fee = this.params.fee; + this.fv = this.params.firstRound; + this.lv = this.params.lastRound; + this.note = undefined; + this.gh = this.params.genesisHash; + const genesisID = ''; + const type = 'axfer'; + + this.assetTestFixture.lastTxn = { + from: accountToUse, + to: accountToUse, + amount: 0, + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + note: this.note, + genesisHash: this.gh, + assetIndex: parseInt(this.assetTestFixture.index), + genesisID, + type, + }; + // update vars used by other helpers + this.txn = this.assetTestFixture.lastTxn; + this.lastRound = this.params.lastRound; + this.pk = accountToUse; + } + ); + + When( + 'I create a transaction transferring {int} assets from creator to a second account', + async function (amount) { + this.params = await this.v2Client.getTransactionParams().do(); + this.fee = this.params.fee; + this.fv = this.params.firstRound; + this.lv = this.params.lastRound; + this.note = undefined; + this.gh = this.params.genesisHash; + const genesisID = ''; + const type = 'axfer'; + + this.assetTestFixture.lastTxn = { + from: this.assetTestFixture.creator, + to: this.accounts[1], + amount: parseInt(amount), + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + note: this.note, + genesisHash: this.gh, + assetIndex: parseInt(this.assetTestFixture.index), + genesisID, + type, + }; + // update vars used by other helpers + this.txn = this.assetTestFixture.lastTxn; + this.lastRound = this.params.lastRound; + this.pk = this.assetTestFixture.creator; + } + ); + + When( + 'I create a transaction transferring {int} assets from a second account to creator', + async function (amount) { + this.params = await this.v2Client.getTransactionParams().do(); + this.fee = this.params.fee; + this.fv = this.params.firstRound; + this.lv = this.params.lastRound; + this.note = undefined; + this.gh = this.params.genesisHash; + const genesisID = ''; + const type = 'axfer'; + + this.assetTestFixture.lastTxn = { + to: this.assetTestFixture.creator, + from: this.accounts[1], + amount: parseInt(amount), + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + note: this.note, + genesisHash: this.gh, + assetIndex: parseInt(this.assetTestFixture.index), + genesisID, + type, + }; + // update vars used by other helpers + this.txn = this.assetTestFixture.lastTxn; + this.lastRound = this.params.lastRound; + [this.pk] = this.accounts; + } + ); + + Then( + 'the creator should have {int} assets remaining', + async function (expectedTotal) { + const accountInformation = await this.v2Client + .accountInformation(this.assetTestFixture.creator) + .do(); + for (const asset of accountInformation.assets) { + if (asset['asset-id'] === this.assetTestFixture.index) { + assert.deepStrictEqual(asset.amount, parseInt(expectedTotal)); + } + } + } + ); + + When('I send the bogus kmd-signed transaction', async function () { + this.err = false; + try { + await this.v2Client.sendRawTransaction(this.stxKmd).do(); + } catch (e) { + this.err = true; + } + }); + + When( + 'I create an un-freeze transaction targeting the second account', + async function () { + this.params = await this.v2Client.getTransactionParams().do(); + this.fee = this.params.fee; + this.fv = this.params.firstRound; + this.lv = this.params.lastRound; + this.note = undefined; + this.gh = this.params.genesisHash; + const freezer = this.assetTestFixture.creator; + + this.assetTestFixture.lastTxn = { + from: freezer, + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + genesisHash: this.gh, + type: 'afrz', + freezeAccount: this.accounts[1], + assetIndex: parseInt(this.assetTestFixture.index), + freezeState: false, + note: this.note, + }; + // update vars used by other helpers + this.txn = this.assetTestFixture.lastTxn; + this.lastRound = this.params.lastRound; + this.pk = this.assetTestFixture.creator; + } + ); + + When( + 'I create a freeze transaction targeting the second account', + async function () { + this.params = await this.v2Client.getTransactionParams().do(); + this.fee = this.params.fee; + this.fv = this.params.firstRound; + this.lv = this.params.lastRound; + this.note = undefined; + this.gh = this.params.genesisHash; + const freezer = this.assetTestFixture.creator; + + this.assetTestFixture.lastTxn = { + from: freezer, + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + genesisHash: this.gh, + type: 'afrz', + freezeAccount: this.accounts[1], + assetIndex: parseInt(this.assetTestFixture.index), + freezeState: true, + note: this.note, + }; + // update vars used by other helpers + this.txn = this.assetTestFixture.lastTxn; + this.lastRound = this.params.lastRound; + this.pk = this.assetTestFixture.creator; + } + ); + + When( + 'I create a transaction revoking {int} assets from a second account to creator', + async function (amount) { + this.params = await this.v2Client.getTransactionParams().do(); + this.fee = this.params.fee; + this.fv = this.params.firstRound; + this.lv = this.params.lastRound; + this.note = undefined; + this.gh = this.params.genesisHash; + const genesisID = ''; + const type = 'axfer'; + + this.assetTestFixture.lastTxn = { + from: this.assetTestFixture.creator, + to: this.assetTestFixture.creator, + assetRevocationTarget: this.accounts[1], + amount: parseInt(amount), + fee: this.fee, + firstRound: this.fv, + lastRound: this.lv, + note: this.note, + genesisHash: this.gh, + assetIndex: parseInt(this.assetTestFixture.index), + genesisID, + type, + }; + // update vars used by other helpers + this.txn = this.assetTestFixture.lastTxn; + this.lastRound = this.params.lastRound; + this.pk = this.assetTestFixture.creator; + } + ); + + /// ///////////////////////////////// + // begin indexer and algodv2 unit tests + /// ///////////////////////////////// + + const globalErrForExamination = ''; + const { + mockAlgodResponderPort, + mockAlgodResponderHost, + mockAlgodPathRecorderPort, + mockAlgodPathRecorderHost, + mockIndexerPathRecorderPort, + mockIndexerPathRecorderHost, + } = options; + + let expectedMockResponse; + let responseFormat; + + Given( + 'mock http responses in {string} loaded from {string}', + function (expectedBody, format) { + if (expectedBody !== null) { + expectedMockResponse = expectedBody; + if (format === 'msgp') { + expectedMockResponse = new Uint8Array( + Buffer.from(expectedMockResponse, 'base64') + ); + } + } + responseFormat = format; + this.v2Client = new algosdk.Algodv2( + '', + `http://${mockAlgodResponderHost}`, + mockAlgodResponderPort, + {} + ); + this.indexerClient = new algosdk.Indexer( + '', + `http://${mockAlgodResponderHost}`, + mockAlgodResponderPort, + {} + ); + } + ); + + Given( + 'mock http responses in {string} loaded from {string} with status {int}.', + function (expectedBody, status, format) { + if (expectedBody !== null) { + expectedMockResponse = expectedBody; + if (format === 'msgp') { + expectedMockResponse = new Uint8Array( + Buffer.from(expectedMockResponse, 'base64') + ); + } + } + responseFormat = format; + this.v2Client = new algosdk.Algodv2( + '', + `http://${mockAlgodResponderHost}`, + mockAlgodResponderPort, + {} + ); + this.indexerClient = new algosdk.Indexer( + '', + `http://${mockAlgodResponderHost}`, + mockAlgodResponderPort, + {} + ); + this.expectedMockResponseCode = status; + } + ); + + When( + 'we make any {string} call to {string}.', + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async function (client, _endpoint) { + try { + if (client === 'algod') { + // endpoints are ignored by mock server, see setupMockServerForResponses + if (responseFormat === 'msgp') { + this.actualMockResponse = await this.v2Client.block(0).do(); + } else { + this.actualMockResponse = await this.v2Client.status().do(); + } + } else if (client === 'indexer') { + // endpoints are ignored by mock server, see setupMockServerForResponses + this.actualMockResponse = await this.indexerClient + .makeHealthCheck() + .do(); + } else { + throw Error(`did not recognize desired client "${client}"`); + } + } catch (err) { + if (this.expectedMockResponseCode === 200) { + throw err; + } + if (this.expectedMockResponseCode === 500) { + if (!err.toString().includes('Received status 500')) { + throw Error( + `expected response code 500 implies error Internal Server Error but instead had error: ${err}` + ); + } + } + } + } + ); + + Then('the parsed response should equal the mock response.', function () { + if (this.expectedMockResponseCode === 200) { + // assert.deepStrictEqual considers a Buffer and Uint8Array with the same contents as unequal. + // These types are fairly interchangable in different parts of the SDK, so we need to normalize + // them before comparing, which is why we chain encoding/decoding below. + if (responseFormat === 'json') { + assert.strictEqual( + JSON.stringify(JSON.parse(expectedMockResponse)), + JSON.stringify(this.actualMockResponse) + ); + } else { + assert.deepStrictEqual( + algosdk.decodeObj( + new Uint8Array(algosdk.encodeObj(this.actualMockResponse)) + ), + algosdk.decodeObj(expectedMockResponse) + ); + } + } + }); + + Then('expect error string to contain {string}', (expectedErrorString) => { + if (expectedErrorString === 'nil') { + assert.strictEqual('', globalErrForExamination); + return; + } + assert.strictEqual(expectedErrorString, globalErrForExamination); + }); + + Given('mock server recording request paths', function () { + this.v2Client = new algosdk.Algodv2( + '', + `http://${mockAlgodPathRecorderHost}`, + mockAlgodPathRecorderPort, + {} + ); + this.indexerClient = new algosdk.Indexer( + '', + `http://${mockIndexerPathRecorderHost}`, + mockIndexerPathRecorderPort, + {} + ); + }); + + Given('expected headers', (tableRows) => { + this.expectedHeaders = tableRows; + }); + + Then( + 'expect the observed header keys to equal the expected header keys', + (algodSeenRequests, indexerSeenRequests) => { + let actualRequests; + if (algodSeenRequests.length !== 0) { + [actualRequests] = algodSeenRequests; + } else if (indexerSeenRequests.length !== 0) { + [actualRequests] = indexerSeenRequests; + } else { + throw new Error('no requests observed.'); + } + const actualHeaders = Object.keys(actualRequests.headers).sort(); + const expectedHeaders = this.expectedHeaders.sort(); + assert.strictEqual( + actualHeaders.length, + expectedHeaders.length, + `expected headers ${expectedHeaders}, got ${actualHeaders}` + ); + for (let i = 0; i < actualHeaders.length; i++) { + assert.deepStrictEqual(actualHeaders[i], expectedHeaders[i]); + } + } + ); + + Then( + 'expect the path used to be {string}', + (algodSeenRequests, indexerSeenRequests, expectedRequestPath) => { + let actualRequest; + if (algodSeenRequests.length !== 0) { + [actualRequest] = algodSeenRequests; + } else if (indexerSeenRequests.length !== 0) { + [actualRequest] = indexerSeenRequests; + } + assert.strictEqual(actualRequest.url, expectedRequestPath); + } + ); + + Then( + 'expect the request to be {string} {string}', + ( + algodSeenRequests, + indexerSeenRequests, + expectedRequestType, + expectedRequestPath + ) => { + let actualRequest; + if (algodSeenRequests.length !== 0) { + [actualRequest] = algodSeenRequests; + } else if (indexerSeenRequests.length !== 0) { + [actualRequest] = indexerSeenRequests; + } + assert.strictEqual( + actualRequest.method.toLowerCase(), + expectedRequestType.toLowerCase() + ); + assert.strictEqual(actualRequest.url, expectedRequestPath); + } + ); + + Then( + 'we expect the path used to be {string}', + (algodSeenRequests, indexerSeenRequests, expectedRequestPath) => { + let actualRequestPath; + if (algodSeenRequests.length !== 0) { + actualRequestPath = algodSeenRequests[0].url; + } else if (indexerSeenRequests.length !== 0) { + actualRequestPath = indexerSeenRequests[0].url; + } + assert.strictEqual(expectedRequestPath, actualRequestPath); + } + ); + + When( + 'we make a Pending Transaction Information against txid {string} with format {string}', + async function (txid, format) { + if (format !== 'msgpack') { + assert.fail('this SDK only supports format msgpack for this function'); + } + await this.v2Client.pendingTransactionInformation(txid).do(); + } + ); + + When( + 'we make a Pending Transaction Information with max {int} and format {string}', + async function (max, format) { + if (format !== 'msgpack') { + assert.fail('this SDK only supports format msgpack for this function'); + } + await this.v2Client.pendingTransactionsInformation().max(max).do(); + } + ); + + When( + 'we make a Pending Transactions By Address call against account {string} and max {int}', + function (account, max) { + this.v2Client.pendingTransactionByAddress(account).max(max).do(); + } + ); + + When( + 'we make a Pending Transactions By Address call against account {string} and max {int} and format {string}', + async function (account, max, format) { + if (format !== 'msgpack') { + assert.fail('this SDK only supports format msgpack for this function'); + } + await this.v2Client.pendingTransactionByAddress(account).max(max).do(); + } + ); + + When( + 'we make a Status after Block call with round {int}', + async function (round) { + await this.v2Client.statusAfterBlock(round).do(); + } + ); + + When( + 'we make an Account Information call against account {string} with exclude {string}', + async function (account, exclude) { + await this.v2Client.accountInformation(account).exclude(exclude).do(); + } + ); + + When( + 'we make an Account Information call against account {string}', + async function (account) { + await this.v2Client.accountInformation(account).do(); + } + ); + + When( + 'we make an Account Asset Information call against account {string} assetID {int}', + async function (account, assetID) { + await this.v2Client.accountAssetInformation(account, assetID).do(); + } + ); + + When( + 'we make an Account Application Information call against account {string} applicationID {int}', + async function (account, applicationID) { + await this.v2Client + .accountApplicationInformation(account, applicationID) + .do(); + } + ); + + When( + 'we make a Get Block call against block number {int}', + function (blockNum) { + this.v2Client.block(blockNum).do(); + } + ); + + When( + 'we make a Get Block call against block number {int} with format {string}', + async function (blockNum, format) { + if (format !== 'msgpack') { + assert.fail('this SDK only supports format msgpack for this function'); + } + await this.v2Client.block(blockNum).do(); + } + ); + + When('we make a GetAssetByID call for assetID {int}', async function (index) { + await this.v2Client.getAssetByID(index).do(); + }); + + When( + 'we make a GetApplicationByID call for applicationID {int}', + async function (index) { + await this.v2Client.getApplicationByID(index).do(); + } + ); + + When( + 'we make a GetApplicationBoxByName call for applicationID {int} with encoded box name {string}', + async function (index, boxName) { + const box = splitAndProcessAppArgs(boxName)[0]; + await this.v2Client.getApplicationBoxByName(index, box).doRaw(); + } + ); + + When( + 'we make a GetApplicationBoxes call for applicationID {int} with max {int}', + async function (index, limit) { + await this.v2Client.getApplicationBoxes(index).max(limit).doRaw(); + } + ); + + let anyPendingTransactionInfoResponse; + + When('we make any Pending Transaction Information call', async function () { + anyPendingTransactionInfoResponse = await this.v2Client + .pendingTransactionInformation() + .do(); + }); + + Then( + 'the parsed Pending Transaction Information response should have sender {string}', + (sender) => { + const actualSender = algosdk.encodeAddress( + anyPendingTransactionInfoResponse.txn.txn.snd + ); + assert.strictEqual(sender, actualSender); + } + ); + + let anyPendingTransactionsInfoResponse; + + When('we make any Pending Transactions Information call', async function () { + anyPendingTransactionsInfoResponse = await this.v2Client + .pendingTransactionsInformation() + .do(); + }); + + Then( + 'the parsed Pending Transactions Information response should contain an array of len {int} and element number {int} should have sender {string}', + (len, idx, sender) => { + assert.strictEqual( + len, + anyPendingTransactionsInfoResponse['top-transactions'].length + ); + if (len !== 0) { + assert.strictEqual( + sender, + algosdk.encodeAddress( + anyPendingTransactionsInfoResponse['top-transactions'][idx].txn.snd + ) + ); + } + } + ); + + let anySendRawTransactionResponse; + + When('we make any Send Raw Transaction call', async function () { + anySendRawTransactionResponse = await this.v2Client + .sendRawTransaction(makeUint8Array(0)) + .do(); + }); + + Then( + 'the parsed Send Raw Transaction response should have txid {string}', + (txid) => { + assert.strictEqual(txid, anySendRawTransactionResponse.txId); + } + ); + + let anyPendingTransactionsByAddressResponse; + + When('we make any Pending Transactions By Address call', async function () { + anyPendingTransactionsByAddressResponse = await this.v2Client + .pendingTransactionByAddress() + .do(); + }); + + Then( + 'the parsed Pending Transactions By Address response should contain an array of len {int} and element number {int} should have sender {string}', + (len, idx, sender) => { + assert.strictEqual( + len, + anyPendingTransactionsByAddressResponse['total-transactions'] + ); + if (len === 0) { + return; + } + let actualSender = + anyPendingTransactionsByAddressResponse['top-transactions'][idx].txn + .snd; + actualSender = algosdk.encodeAddress(actualSender); + assert.strictEqual(sender, actualSender); + } + ); + + let anyNodeStatusResponse; + + When('we make any Node Status call', async function () { + anyNodeStatusResponse = await this.v2Client.status().do(); + }); + + Then( + 'the parsed Node Status response should have a last round of {int}', + (lastRound) => { + assert.strictEqual(lastRound, anyNodeStatusResponse['last-round']); + } + ); + + let anyLedgerSupplyResponse; + + When('we make any Ledger Supply call', async function () { + anyLedgerSupplyResponse = await this.v2Client.supply().do(); + }); + + Then( + 'the parsed Ledger Supply response should have totalMoney {int} onlineMoney {int} on round {int}', + (totalMoney, onlineMoney, round) => { + assert.strictEqual(totalMoney, anyLedgerSupplyResponse['total-money']); + assert.strictEqual(onlineMoney, anyLedgerSupplyResponse['online-money']); + assert.strictEqual(round, anyLedgerSupplyResponse.current_round); + } + ); + + When('we make any Status After Block call', async function () { + anyNodeStatusResponse = await this.v2Client.statusAfterBlock(1).do(); + }); + + Then( + 'the parsed Status After Block response should have a last round of {int}', + (lastRound) => { + assert.strictEqual(lastRound, anyNodeStatusResponse['last-round']); + } + ); + + let anyAccountInformationResponse; + + When('we make any Account Information call', async function () { + anyAccountInformationResponse = await this.v2Client + .accountInformation() + .do(); + }); + + Then( + 'the parsed Account Information response should have address {string}', + (address) => { + assert.strictEqual(address, anyAccountInformationResponse.address); + } + ); + + let anyBlockResponse; + + When('we make any Get Block call', async function () { + anyBlockResponse = await this.v2Client.block(1).do(); + }); + + Then( + 'the parsed Get Block response should have rewards pool {string}', + (rewardsPoolAddress) => { + const rewardsPoolB64String = Buffer.from( + anyBlockResponse.block.rwd + ).toString('base64'); + assert.strictEqual(rewardsPoolAddress, rewardsPoolB64String); + } + ); + + let anySuggestedTransactionsResponse; + + When('we make any Suggested Transaction Parameters call', async function () { + anySuggestedTransactionsResponse = await this.v2Client + .getTransactionParams() + .do(); + }); + + Then( + 'the parsed Suggested Transaction Parameters response should have first round valid of {int}', + (firstRound) => { + assert.strictEqual( + firstRound, + anySuggestedTransactionsResponse.firstRound + ); + } + ); + + When( + 'we make a Lookup Asset Balances call against asset index {int} with limit {int} afterAddress {string} currencyGreaterThan {int} currencyLessThan {int}', + async function ( + index, + limit, + afterAddress, + currencyGreater, + currencyLesser + ) { + await this.indexerClient + .lookupAssetBalances(index) + .limit(limit) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .do(); + } + ); + + When( + 'we make a Lookup Asset Transactions call against asset index {int} with NotePrefix {string} TxType {string} SigType {string} txid {string} round {int} minRound {int} maxRound {int} limit {int} beforeTime {string} afterTime {string} currencyGreaterThan {int} currencyLessThan {int} address {string} addressRole {string} ExcluseCloseTo {string}', + async function ( + assetIndex, + notePrefix, + txType, + sigType, + txid, + round, + minRound, + maxRound, + limit, + beforeTime, + afterTime, + currencyGreater, + currencyLesser, + address, + addressRole, + excludeCloseToAsString + ) { + let excludeCloseTo = false; + if (excludeCloseToAsString === 'true') { + excludeCloseTo = true; + } + await this.indexerClient + .lookupAssetTransactions(assetIndex) + .notePrefix(notePrefix) + .txType(txType) + .sigType(sigType) + .txid(txid) + .round(round) + .minRound(minRound) + .maxRound(maxRound) + .limit(limit) + .beforeTime(beforeTime) + .afterTime(afterTime) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .address(address) + .addressRole(addressRole) + .excludeCloseTo(excludeCloseTo) + .do(); + } + ); + + When( + 'we make a Lookup Asset Transactions call against asset index {int} with NotePrefix {string} TxType {string} SigType {string} txid {string} round {int} minRound {int} maxRound {int} limit {int} beforeTime {string} afterTime {string} currencyGreaterThan {int} currencyLessThan {int} address {string} addressRole {string} ExcluseCloseTo {string} RekeyTo {string}', + async function ( + assetIndex, + notePrefix, + txType, + sigType, + txid, + round, + minRound, + maxRound, + limit, + beforeTime, + afterTime, + currencyGreater, + currencyLesser, + address, + addressRole, + excludeCloseToAsString, + rekeyToAsString + ) { + let excludeCloseTo = false; + if (excludeCloseToAsString === 'true') { + excludeCloseTo = true; + } + let rekeyTo = false; + if (rekeyToAsString === 'true') { + rekeyTo = true; + } + await this.indexerClient + .lookupAssetTransactions(assetIndex) + .notePrefix(notePrefix) + .txType(txType) + .sigType(sigType) + .txid(txid) + .round(round) + .minRound(minRound) + .maxRound(maxRound) + .limit(limit) + .beforeTime(beforeTime) + .afterTime(afterTime) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .address(address) + .addressRole(addressRole) + .excludeCloseTo(excludeCloseTo) + .rekeyTo(rekeyTo) + .do(); + } + ); + + When( + 'we make a Lookup Account Transactions call against account {string} with NotePrefix {string} TxType {string} SigType {string} txid {string} round {int} minRound {int} maxRound {int} limit {int} beforeTime {string} afterTime {string} currencyGreaterThan {int} currencyLessThan {int} assetIndex {int}', + async function ( + account, + notePrefix, + txType, + sigType, + txid, + round, + minRound, + maxRound, + limit, + beforeTime, + afterTime, + currencyGreater, + currencyLesser, + assetIndex + ) { + await this.indexerClient + .lookupAccountTransactions(account) + .notePrefix(notePrefix) + .txType(txType) + .sigType(sigType) + .txid(txid) + .round(round) + .minRound(minRound) + .maxRound(maxRound) + .limit(limit) + .beforeTime(beforeTime) + .afterTime(afterTime) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .assetID(assetIndex) + .do(); + } + ); + + When( + 'we make a Lookup Account Transactions call against account {string} with NotePrefix {string} TxType {string} SigType {string} txid {string} round {int} minRound {int} maxRound {int} limit {int} beforeTime {string} afterTime {string} currencyGreaterThan {int} currencyLessThan {int} assetIndex {int} rekeyTo {string}', + async function ( + account, + notePrefix, + txType, + sigType, + txid, + round, + minRound, + maxRound, + limit, + beforeTime, + afterTime, + currencyGreater, + currencyLesser, + assetIndex, + rekeyToAsString + ) { + let rekeyTo = false; + if (rekeyToAsString === 'true') { + rekeyTo = true; + } + await this.indexerClient + .lookupAccountTransactions(account) + .notePrefix(notePrefix) + .txType(txType) + .sigType(sigType) + .txid(txid) + .round(round) + .minRound(minRound) + .maxRound(maxRound) + .limit(limit) + .beforeTime(beforeTime) + .afterTime(afterTime) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .assetID(assetIndex) + .rekeyTo(rekeyTo) + .do(); + } + ); + + When( + 'we make a Lookup Block call against round {int}', + async function (round) { + await this.indexerClient.lookupBlock(round).do(); + } + ); + + When( + 'we make a Lookup Block call against round {int} and header {string}', + async function (int, string) { + await this.indexerClient.lookupBlock(int).headerOnly(string).do(); + } + ); + + When( + 'we make a Lookup Account by ID call against account {string} with round {int}', + async function (account, round) { + await this.indexerClient.lookupAccountByID(account).round(round).do(); + } + ); + + When( + 'we make a Lookup Account by ID call against account {string} with exclude {string}', + async function (account, exclude) { + await this.indexerClient.lookupAccountByID(account).exclude(exclude).do(); + } + ); + + When( + 'we make a Lookup Asset by ID call against asset index {int}', + async function (assetIndex) { + await this.indexerClient.lookupAssetByID(assetIndex).do(); + } + ); + + When( + 'we make a SearchForApplicationBoxes call with applicationID {int} with max {int} nextToken {string}', + async function (index, limit, token) { + await this.indexerClient + .searchForApplicationBoxes(index) + .limit(limit) + .nextToken(token) + .doRaw(); + } + ); + + When( + 'we make a LookupApplicationBoxByIDandName call with applicationID {int} with encoded box name {string}', + async function (index, name) { + const boxKey = splitAndProcessAppArgs(name)[0]; + await this.indexerClient + .lookupApplicationBoxByIDandName(index, boxKey) + .doRaw(); + } + ); + + When( + 'we make a LookupApplicationLogsByID call with applicationID {int} limit {int} minRound {int} maxRound {int} nextToken {string} sender {string} and txID {string}', + async function (appID, limit, minRound, maxRound, nextToken, sender, txID) { + await this.indexerClient + .lookupApplicationLogs(appID) + .limit(limit) + .maxRound(maxRound) + .minRound(minRound) + .nextToken(nextToken) + .sender(sender) + .txid(txID) + .do(); + } + ); + + When( + 'we make a Search Accounts call with assetID {int} limit {int} currencyGreaterThan {int} currencyLessThan {int} and round {int}', + async function (assetIndex, limit, currencyGreater, currencyLesser, round) { + await this.indexerClient + .searchAccounts() + .assetID(assetIndex) + .limit(limit) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .round(round) + .do(); + } + ); + + When( + 'we make a Search Accounts call with assetID {int} limit {int} currencyGreaterThan {int} currencyLessThan {int} round {int} and authenticating address {string}', + async function ( + assetIndex, + limit, + currencyGreater, + currencyLesser, + round, + authAddress + ) { + await this.indexerClient + .searchAccounts() + .assetID(assetIndex) + .limit(limit) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .round(round) + .authAddr(authAddress) + .do(); + } + ); + + When( + 'we make a Search Accounts call with exclude {string}', + async function (exclude) { + await this.indexerClient.searchAccounts().exclude(exclude).do(); + } + ); + + When( + 'we make a SearchForApplications call with creator {string}', + async function (creator) { + await this.indexerClient.searchForApplications().creator(creator).do(); + } + ); + + When( + 'we make a Search For Transactions call with account {string} NotePrefix {string} TxType {string} SigType {string} txid {string} round {int} minRound {int} maxRound {int} limit {int} beforeTime {string} afterTime {string} currencyGreaterThan {int} currencyLessThan {int} assetIndex {int} addressRole {string} ExcluseCloseTo {string}', + async function ( + account, + notePrefix, + txType, + sigType, + txid, + round, + minRound, + maxRound, + limit, + beforeTime, + afterTime, + currencyGreater, + currencyLesser, + assetIndex, + addressRole, + excludeCloseToAsString + ) { + let excludeCloseTo = false; + if (excludeCloseToAsString === 'true') { + excludeCloseTo = true; + } + await this.indexerClient + .searchForTransactions() + .address(account) + .notePrefix(notePrefix) + .txType(txType) + .sigType(sigType) + .txid(txid) + .round(round) + .minRound(minRound) + .maxRound(maxRound) + .limit(limit) + .beforeTime(beforeTime) + .afterTime(afterTime) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .assetID(assetIndex) + .addressRole(addressRole) + .excludeCloseTo(excludeCloseTo) + .do(); + } + ); + + When( + 'we make a Search For Transactions call with account {string} NotePrefix {string} TxType {string} SigType {string} txid {string} round {int} minRound {int} maxRound {int} limit {int} beforeTime {string} afterTime {string} currencyGreaterThan {int} currencyLessThan {int} assetIndex {int} addressRole {string} ExcluseCloseTo {string} rekeyTo {string}', + async function ( + account, + notePrefix, + txType, + sigType, + txid, + round, + minRound, + maxRound, + limit, + beforeTime, + afterTime, + currencyGreater, + currencyLesser, + assetIndex, + addressRole, + excludeCloseToAsString, + rekeyToAsString + ) { + let excludeCloseTo = false; + if (excludeCloseToAsString === 'true') { + excludeCloseTo = true; + } + let rekeyTo = false; + if (rekeyToAsString === 'true') { + rekeyTo = true; + } + await this.indexerClient + .searchForTransactions() + .address(account) + .notePrefix(notePrefix) + .txType(txType) + .sigType(sigType) + .txid(txid) + .round(round) + .minRound(minRound) + .maxRound(maxRound) + .limit(limit) + .beforeTime(beforeTime) + .afterTime(afterTime) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .assetID(assetIndex) + .addressRole(addressRole) + .excludeCloseTo(excludeCloseTo) + .rekeyTo(rekeyTo) + .do(); + } + ); + + When( + 'we make a SearchForAssets call with limit {int} creator {string} name {string} unit {string} index {int}', + async function (limit, creator, name, unit, index) { + await this.indexerClient + .searchForAssets() + .limit(limit) + .creator(creator) + .name(name) + .unit(unit) + .index(index) + .do(); + } + ); + + When( + 'we make a SearchForApplications call with applicationID {int}', + async function (index) { + await this.indexerClient.searchForApplications().index(index).do(); + } + ); + + When( + 'we make a LookupApplications call with applicationID {int}', + async function (index) { + await this.indexerClient.lookupApplications(index).do(); + } + ); + + let anyLookupAssetBalancesResponse; + + When('we make any LookupAssetBalances call', async function () { + anyLookupAssetBalancesResponse = await this.indexerClient + .lookupAssetBalances() + .setIntDecoding('mixed') + .do(); + }); + + Then( + 'the parsed LookupAssetBalances response should be valid on round {int}, and contain an array of len {int} and element number {int} should have address {string} amount {int} and frozen state {string}', + (round, length, idx, address, amount, frozenStateAsString) => { + assert.strictEqual( + round, + anyLookupAssetBalancesResponse['current-round'] + ); + assert.strictEqual( + length, + anyLookupAssetBalancesResponse.balances.length + ); + if (length === 0) { + return; + } + let frozenState = false; + if (frozenStateAsString === 'true') { + frozenState = true; + } + assert.strictEqual( + amount, + anyLookupAssetBalancesResponse.balances[idx].amount + ); + assert.strictEqual( + frozenState, + anyLookupAssetBalancesResponse.balances[idx]['is-frozen'] + ); + } + ); + + When( + 'we make a LookupAccountAssets call with accountID {string} assetID {int} includeAll {string} limit {int} next {string}', + async function (account, assetID, includeAll, limit, next) { + await this.indexerClient + .lookupAccountAssets(account) + .assetId(assetID) + .includeAll(includeAll === 'true') + .limit(limit) + .nextToken(next) + .do(); + } + ); + + When( + 'we make a LookupAccountCreatedAssets call with accountID {string} assetID {int} includeAll {string} limit {int} next {string}', + async function (account, assetID, includeAll, limit, next) { + await this.indexerClient + .lookupAccountCreatedAssets(account) + .assetID(assetID) + .includeAll(includeAll === 'true') + .limit(limit) + .nextToken(next) + .do(); + } + ); + + When( + 'we make a LookupAccountAppLocalStates call with accountID {string} applicationID {int} includeAll {string} limit {int} next {string}', + async function (account, applicationID, includeAll, limit, next) { + await this.indexerClient + .lookupAccountAppLocalStates(account) + .applicationID(applicationID) + .includeAll(includeAll === 'true') + .limit(limit) + .nextToken(next) + .do(); + } + ); + + When( + 'we make a LookupAccountCreatedApplications call with accountID {string} applicationID {int} includeAll {string} limit {int} next {string}', + async function (account, applicationID, includeAll, limit, next) { + await this.indexerClient + .lookupAccountCreatedApplications(account) + .applicationID(applicationID) + .includeAll(includeAll === 'true') + .limit(limit) + .nextToken(next) + .do(); + } + ); + + let anyLookupAssetTransactionsResponse; + + When('we make any LookupAssetTransactions call', async function () { + anyLookupAssetTransactionsResponse = await this.indexerClient + .lookupAssetTransactions() + .setIntDecoding('mixed') + .do(); + }); + + Then( + 'the parsed LookupAssetTransactions response should be valid on round {int}, and contain an array of len {int} and element number {int} should have sender {string}', + (round, length, idx, sender) => { + assert.strictEqual( + round, + anyLookupAssetTransactionsResponse['current-round'] + ); + assert.strictEqual( + length, + anyLookupAssetTransactionsResponse.transactions.length + ); + if (length === 0) { + return; + } + assert.strictEqual( + sender, + anyLookupAssetTransactionsResponse.transactions[idx].sender + ); + } + ); + + let anyLookupAccountTransactionsResponse; + + When('we make any LookupAccountTransactions call', async function () { + anyLookupAccountTransactionsResponse = await this.indexerClient + .lookupAccountTransactions() + .do(); + }); + + Then( + 'the parsed LookupAccountTransactions response should be valid on round {int}, and contain an array of len {int} and element number {int} should have sender {string}', + (round, length, idx, sender) => { + assert.strictEqual( + round, + anyLookupAccountTransactionsResponse['current-round'] + ); + assert.strictEqual( + length, + anyLookupAccountTransactionsResponse.transactions.length + ); + if (length === 0) { + return; + } + assert.strictEqual( + sender, + anyLookupAccountTransactionsResponse.transactions[idx].sender + ); + } + ); + + let anyLookupBlockResponse; + + When('we make any LookupBlock call', async function () { + anyLookupBlockResponse = await this.indexerClient.lookupBlock().do(); + }); + + Then( + 'the parsed LookupBlock response should have previous block hash {string}', + (prevHash) => { + assert.strictEqual( + prevHash, + anyLookupBlockResponse['previous-block-hash'] + ); + } + ); + + let anyLookupAccountByIDResponse; + + When('we make any LookupAccountByID call', async function () { + anyLookupAccountByIDResponse = await this.indexerClient + .lookupAccountByID() + .do(); + }); + + Then( + 'the parsed LookupAccountByID response should have address {string}', + (address) => { + assert.strictEqual(address, anyLookupAccountByIDResponse.account.address); + } + ); + + let anyLookupAssetByIDResponse; + + When('we make any LookupAssetByID call', async function () { + anyLookupAssetByIDResponse = await this.indexerClient + .lookupAssetByID() + .setIntDecoding('mixed') + .do(); + }); + + Then('the parsed LookupAssetByID response should have index {int}', (idx) => { + assert.strictEqual(idx, anyLookupAssetByIDResponse.asset.index); + }); + + let anySearchAccountsResponse; + + When('we make any SearchAccounts call', async function () { + anySearchAccountsResponse = await this.indexerClient.searchAccounts().do(); + }); + + Then( + 'the parsed SearchAccounts response should be valid on round {int} and the array should be of len {int} and the element at index {int} should have address {string}', + (round, length, idx, address) => { + assert.strictEqual(round, anySearchAccountsResponse['current-round']); + assert.strictEqual(length, anySearchAccountsResponse.accounts.length); + if (length === 0) { + return; + } + assert.strictEqual( + address, + anySearchAccountsResponse.accounts[idx].address + ); + } + ); + + Then( + 'the parsed SearchAccounts response should be valid on round {int} and the array should be of len {int} and the element at index {int} should have authorizing address {string}', + (round, length, idx, authAddress) => { + assert.strictEqual(round, anySearchAccountsResponse['current-round']); + assert.strictEqual(length, anySearchAccountsResponse.accounts.length); + if (length === 0) { + return; + } + assert.strictEqual( + authAddress, + anySearchAccountsResponse.accounts[idx]['auth-addr'] + ); + } + ); + + let anySearchForTransactionsResponse; + + When('we make any SearchForTransactions call', async function () { + anySearchForTransactionsResponse = await this.indexerClient + .searchForTransactions() + .do(); + }); + + Then( + 'the parsed SearchForTransactions response should be valid on round {int} and the array should be of len {int} and the element at index {int} should have sender {string}', + (round, length, idx, sender) => { + assert.strictEqual( + round, + anySearchForTransactionsResponse['current-round'] + ); + assert.strictEqual( + length, + anySearchForTransactionsResponse.transactions.length + ); + if (length === 0) { + return; + } + assert.strictEqual( + sender, + anySearchForTransactionsResponse.transactions[idx].sender + ); + } + ); + + Then( + 'the parsed SearchForTransactions response should be valid on round {int} and the array should be of len {int} and the element at index {int} should have rekey-to {string}', + (round, length, idx, rekeyTo) => { + assert.strictEqual( + round, + anySearchForTransactionsResponse['current-round'] + ); + assert.strictEqual( + length, + anySearchForTransactionsResponse.transactions.length + ); + if (length === 0) { + return; + } + assert.strictEqual( + rekeyTo, + anySearchForTransactionsResponse.transactions[idx]['rekey-to'] + ); + } + ); + + let anySearchForAssetsResponse; + + When('we make any SearchForAssets call', async function () { + anySearchForAssetsResponse = await this.indexerClient + .searchForAssets() + .do(); + }); + + Then( + 'the parsed SearchForAssets response should be valid on round {int} and the array should be of len {int} and the element at index {int} should have asset index {int}', + (round, length, idx, assetIndex) => { + assert.strictEqual(round, anySearchForAssetsResponse['current-round']); + assert.strictEqual(length, anySearchForAssetsResponse.assets.length); + if (length === 0) { + return; + } + assert.strictEqual( + assetIndex, + anySearchForAssetsResponse.assets[idx].index + ); + } + ); + + /// ///////////////////////////////// + // begin rekey test helpers + /// ///////////////////////////////// + + When('I add a rekeyTo field with address {string}', function (address) { + this.txn.reKeyTo = address; + }); + + When( + 'I add a rekeyTo field with the private key algorand address', + function () { + const keypair = keyPairFromSecretKey(this.sk); + const pubKeyFromSk = keypair.publicKey; + this.txn.reKeyTo = algosdk.encodeAddress(pubKeyFromSk); + } + ); + + When('I set the from address to {string}', function (from) { + this.txn.from = from; + }); + + let dryrunResponse; + + When('we make any Dryrun call', async function () { + const dr = new algosdk.modelsv2.DryrunRequest({}); + dryrunResponse = await this.v2Client.dryrun(dr).do(); + }); + + Then( + 'the parsed Dryrun Response should have global delta {string} with {int}', + (key, action) => { + assert.strictEqual(dryrunResponse.txns[0]['global-delta'][0].key, key); + assert.strictEqual( + dryrunResponse.txns[0]['global-delta'][0].value.action, + action + ); + } + ); + + When('I dryrun a {string} program {string}', async function (kind, program) { + const data = await loadResource(program); + const algoTxn = new algosdk.Transaction({ + from: 'UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M', + fee: 1000, + amount: 1000, + firstRound: 1, + lastRound: 1000, + type: 'pay', + genesisHash: 'ZIkPs8pTDxbRJsFB1yJ7gvnpDu0Q85FRkl2NCkEAQLU=', + }); + let txns; + let sources; + + switch (kind) { + case 'compiled': + txns = [ + { + lsig: new algosdk.LogicSig(data), + txn: algoTxn, + }, + ]; + break; + case 'source': + txns = [ + { + txn: algoTxn, + }, + ]; + sources = [ + new algosdk.modelsv2.DryrunSource({ + fieldName: 'lsig', + source: data.toString('utf8'), + txnIndex: 0, + }), + ]; + break; + default: + throw Error(`kind ${kind} not in (source, compiled)`); + } + + const dr = new algosdk.modelsv2.DryrunRequest({ + txns, + sources, + }); + dryrunResponse = await this.v2Client.dryrun(dr).do(); + }); + + Then('I get execution result {string}', (result) => { + let msgs; + const res = dryrunResponse.txns[0]; + if ( + res['logic-sig-messages'] !== undefined && + res['logic-sig-messages'].length > 0 + ) { + msgs = res['logic-sig-messages']; + } else if ( + res['app-call-messages'] !== undefined && + res['app-call-messages'].length > 0 + ) { + msgs = res['app-call-messages']; + } + assert.ok(msgs.length > 0); + assert.strictEqual(msgs[0], result); + }); + + let compileStatusCode; + let compileResponse; + + When('I compile a teal program {string}', async function (program) { + const data = await loadResource(program); + try { + compileResponse = await this.v2Client.compile(data).do(); + compileStatusCode = 200; + } catch (e) { + compileStatusCode = e.response.status; + compileResponse = { + result: '', + hash: '', + }; + } + }); + + Then( + 'it is compiled with {int} and {string} and {string}', + (status, result, hash) => { + assert.strictEqual(status, compileStatusCode); + assert.strictEqual(result, compileResponse.result); + assert.strictEqual(hash, compileResponse.hash); + } + ); + + Then( + 'base64 decoding the response is the same as the binary {string}', + async (program) => { + const data = await loadResource(program); + const decodedResult = makeUint8Array( + Buffer.from(compileResponse.result, 'base64') + ); + assert.deepStrictEqual(makeUint8Array(data), decodedResult); + } + ); + + /// ///////////////////////////////// + // TealSign tests + /// ///////////////////////////////// + + Given('base64 encoded data to sign {string}', function (data) { + this.data = Buffer.from(data, 'base64'); + }); + + Given('program hash {string}', function (contractAddress) { + this.contractAddress = contractAddress; + }); + + Given('base64 encoded program {string}', function (programEncoded) { + const program = Buffer.from(programEncoded, 'base64'); + const lsig = new algosdk.LogicSig(program); + this.contractAddress = lsig.address(); + }); + + Given('base64 encoded private key {string}', function (keyEncoded) { + const seed = Buffer.from(keyEncoded, 'base64'); + const keys = keyPairFromSeed(seed); + this.sk = keys.secretKey; + }); + + When('I perform tealsign', function () { + this.sig = algosdk.tealSign(this.sk, this.data, this.contractAddress); + }); + + Then('the signature should be equal to {string}', function (expectedEncoded) { + const expected = makeUint8Array(Buffer.from(expectedEncoded, 'base64')); + assert.deepStrictEqual(this.sig, expected); + }); + + /// ///////////////////////////////// + // begin application test helpers + /// ///////////////////////////////// + + Given( + 'a signing account with address {string} and mnemonic {string}', + function (address, mnemonic) { + this.signingAccount = algosdk.mnemonicToSecretKey(mnemonic); + if (this.signingAccount.addr !== address) { + throw new Error( + `Address does not match mnemonic: ${this.signingAccount.addr} !== ${address}` + ); + } + } + ); + + Given( + 'suggested transaction parameters from the algod v2 client', + async function () { + this.suggestedParams = await this.v2Client.getTransactionParams().do(); + } + ); + + When( + 'I build a payment transaction with sender {string}, receiver {string}, amount {int}, close remainder to {string}', + function (sender, receiver, amount, closeTo) { + const from = sender === 'transient' ? this.transientAccount.addr : sender; + const to = + receiver === 'transient' ? this.transientAccount.addr : receiver; + + this.txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from, + to, + amount: parseInt(amount, 10), + closeRemainderTo: closeTo.length === 0 ? undefined : closeTo, + suggestedParams: this.suggestedParams, + }); + } + ); + + Then( + "I get the account address for the current application and see that it matches the app id's hash", + async function () { + const appID = this.currentApplicationIndex; + const toSign = Buffer.concat([ + Buffer.from('appID'), + algosdk.encodeUint64(appID), + ]); + const expected = algosdk.encodeAddress( + makeUint8Array(genericHash(toSign)) + ); + const actual = algosdk.getApplicationAddress(appID); + assert.strictEqual(expected, actual); + } + ); + + Given( + "I fund the current application's address with {int} microalgos.", + async function (amount) { + const sp = await this.v2Client.getTransactionParams().do(); + if (sp.firstRound === 0) sp.firstRound = 1; + const fundingTxnArgs = { + from: this.accounts[0], + to: algosdk.getApplicationAddress(this.currentApplicationIndex), + amount, + suggestedParams: sp, + }; + const stxn = await this.kcl.signTransaction( + this.handle, + this.wallet_pswd, + fundingTxnArgs + ); + + const fundingResponse = await this.v2Client.sendRawTransaction(stxn).do(); + const info = await algosdk.waitForConfirmation( + this.v2Client, + fundingResponse.txId, + 1 + ); + assert.ok(info['confirmed-round'] > 0); + } + ); + + Given( + 'suggested transaction parameters fee {int}, flat-fee {string}, first-valid {int}, last-valid {int}, genesis-hash {string}, genesis-id {string}', + function (fee, flatFee, firstRound, lastRound, genesisHash, genesisID) { + assert.ok(['true', 'false'].includes(flatFee)); + + this.suggestedParams = { + flatFee: flatFee === 'true', + fee, + firstRound, + lastRound, + genesisID, + genesisHash, + }; + } + ); + + When( + 'I build a keyreg transaction with sender {string}, nonparticipation {string}, vote first {int}, vote last {int}, key dilution {int}, vote public key {string}, selection public key {string}, and state proof public key {string}', + function ( + sender, + nonpart, + voteFirst, + voteLast, + keyDilution, + votePk, + selectionPk, + stateProofPk + ) { + assert.ok(['true', 'false'].includes(nonpart)); + + this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParams( + sender, + undefined, + votePk.length ? votePk : undefined, + selectionPk.length ? selectionPk : undefined, + voteFirst, + voteLast, + keyDilution, + this.suggestedParams, + undefined, + nonpart === 'true', + stateProofPk.length ? stateProofPk : undefined + ); + } + ); + + function operationStringToEnum(inString) { + switch (inString) { + case 'noop': + return algosdk.OnApplicationComplete.NoOpOC; + case 'call': + return algosdk.OnApplicationComplete.NoOpOC; + case 'create': + return algosdk.OnApplicationComplete.NoOpOC; + case 'update': + return algosdk.OnApplicationComplete.UpdateApplicationOC; + case 'optin': + return algosdk.OnApplicationComplete.OptInOC; + case 'delete': + return algosdk.OnApplicationComplete.DeleteApplicationOC; + case 'clear': + return algosdk.OnApplicationComplete.ClearStateOC; + case 'closeout': + return algosdk.OnApplicationComplete.CloseOutOC; + default: + throw Error( + `did not recognize application operation string ${inString}` + ); + } + } + + async function compileProgram(client, program) { + const data = await loadResource(program); + if (program.endsWith('.teal')) { + try { + const compiledResponse = await client.compile(data).do(); + const compiledProgram = makeUint8Array( + Buffer.from(compiledResponse.result, 'base64') + ); + return compiledProgram; + } catch (err) { + throw new Error(`could not compile teal program: ${err}`); + } + } + return makeUint8Array(data); + } + + When( + 'I build an application transaction with operation {string}, application-id {int}, sender {string}, approval-program {string}, clear-program {string}, global-bytes {int}, global-ints {int}, local-bytes {int}, local-ints {int}, app-args {string}, foreign-apps {string}, foreign-assets {string}, app-accounts {string}, fee {int}, first-valid {int}, last-valid {int}, genesis-hash {string}, extra-pages {int}, boxes {string}', + async function ( + operationString, + appIndex, + sender, + approvalProgramFile, + clearProgramFile, + numGlobalByteSlices, + numGlobalInts, + numLocalByteSlices, + numLocalInts, + appArgsCommaSeparatedString, + foreignAppsCommaSeparatedString, + foreignAssetsCommaSeparatedString, + appAccountsCommaSeparatedString, + fee, + firstValid, + lastValid, + genesisHashBase64, + extraPages, + boxesCommaSeparatedString + ) { + // operation string to enum + const operation = operationStringToEnum(operationString); + // open and load in approval program + let approvalProgramBytes; + if (approvalProgramFile !== '') { + approvalProgramBytes = await compileProgram( + this.v2Client, + approvalProgramFile + ); + } + // open and load in clear program + let clearProgramBytes; + if (clearProgramFile !== '') { + clearProgramBytes = await compileProgram( + this.v2Client, + clearProgramFile + ); + } + // split and process app args + let appArgs; + if (appArgsCommaSeparatedString !== '') { + appArgs = splitAndProcessAppArgs(appArgsCommaSeparatedString); + } + // split and process foreign apps + let foreignApps; + if (foreignAppsCommaSeparatedString !== '') { + foreignApps = makeArray(); + foreignAppsCommaSeparatedString + .split(',') + .forEach((foreignAppAsString) => { + foreignApps.push(parseInt(foreignAppAsString)); + }); + } + // split and process foreign assets + let foreignAssets; + if (foreignAssetsCommaSeparatedString !== '') { + foreignAssets = makeArray(); + foreignAssetsCommaSeparatedString + .split(',') + .forEach((foreignAssetAsString) => { + foreignAssets.push(parseInt(foreignAssetAsString)); + }); + } + // split and process app accounts + let appAccounts; + if (appAccountsCommaSeparatedString !== '') { + appAccounts = makeArray(...appAccountsCommaSeparatedString.split(',')); + } + // split and process box references + let boxes; + if (boxesCommaSeparatedString !== '') { + boxes = splitAndProcessBoxReferences(boxesCommaSeparatedString); + } + // build suggested params object + const sp = { + genesisHash: genesisHashBase64, + firstRound: firstValid, + lastRound: lastValid, + fee, + flatFee: true, + }; + + switch (operationString) { + case 'call': + this.txn = algosdk.makeApplicationNoOpTxn( + sender, + sp, + appIndex, + appArgs, + appAccounts, + foreignApps, + foreignAssets, + undefined, + undefined, + undefined, + boxes + ); + return; + case 'create': + this.txn = algosdk.makeApplicationCreateTxn( + sender, + sp, + operation, + approvalProgramBytes, + clearProgramBytes, + numLocalInts, + numLocalByteSlices, + numGlobalInts, + numGlobalByteSlices, + appArgs, + appAccounts, + foreignApps, + foreignAssets, + undefined, + undefined, + undefined, + extraPages, + boxes + ); + return; + case 'update': + this.txn = algosdk.makeApplicationUpdateTxn( + sender, + sp, + appIndex, + approvalProgramBytes, + clearProgramBytes, + appArgs, + appAccounts, + foreignApps, + foreignAssets, + undefined, + undefined, + undefined, + boxes + ); + return; + case 'optin': + this.txn = algosdk.makeApplicationOptInTxn( + sender, + sp, + appIndex, + appArgs, + appAccounts, + foreignApps, + foreignAssets, + undefined, + undefined, + undefined, + boxes + ); + return; + case 'delete': + this.txn = algosdk.makeApplicationDeleteTxn( + sender, + sp, + appIndex, + appArgs, + appAccounts, + foreignApps, + foreignAssets, + undefined, + undefined, + undefined, + boxes + ); + return; + case 'clear': + this.txn = algosdk.makeApplicationClearStateTxn( + sender, + sp, + appIndex, + appArgs, + appAccounts, + foreignApps, + foreignAssets, + boxes + ); + return; + case 'closeout': + this.txn = algosdk.makeApplicationCloseOutTxn( + sender, + sp, + appIndex, + appArgs, + appAccounts, + foreignApps, + foreignAssets, + undefined, + undefined, + undefined, + boxes + ); + return; + default: + throw Error( + `did not recognize application operation string ${operationString}` + ); + } + } + ); + + When('sign the transaction', function () { + this.stx = this.txn.signTxn(this.signingAccount.sk); + }); + + Then( + 'the base64 encoded signed transaction should equal {string}', + function (base64golden) { + const actualBase64 = Buffer.from(this.stx).toString('base64'); + assert.strictEqual(actualBase64, base64golden); + } + ); + + Then('the decoded transaction should equal the original', function () { + const decoded = algosdk.decodeSignedTransaction(this.stx); + // comparing the output of get_obj_for_encoding instead because the Transaction class instance + // may have some nonconsequential differences in internal representation + assert.deepStrictEqual( + decoded.txn.get_obj_for_encoding(), + this.txn.get_obj_for_encoding() + ); + }); + + Given( + 'an algod v2 client connected to {string} port {int} with token {string}', + function (host, port, token) { + let mutableHost = host; + + if (!mutableHost.startsWith('http')) { + mutableHost = `http://${mutableHost}`; + } + this.v2Client = new algosdk.Algodv2(token, mutableHost, port, {}); + } + ); + + Given( + 'an algod v2 client connected to mock server with token {string}', + function (token) { + this.v2Client = new algosdk.Algodv2( + token, + `http://${mockAlgodPathRecorderHost}`, + mockAlgodPathRecorderPort, + {} + ); + } + ); + + Given( + 'an indexer v2 client connected to mock server with token {string}', + function (token) { + this.indexerV2client = new algosdk.Indexer( + token, + `http://${mockIndexerPathRecorderHost}`, + mockIndexerPathRecorderPort, + {} + ); + } + ); + + Given( + 'I create a new transient account and fund it with {int} microalgos.', + async function (fundingAmount) { + this.transientAccount = algosdk.generateAccount(); + + const sp = await this.v2Client.getTransactionParams().do(); + if (sp.firstRound === 0) sp.firstRound = 1; + const fundingTxnArgs = { + from: this.accounts[0], + to: this.transientAccount.addr, + amount: fundingAmount, + suggestedParams: sp, + }; + const stxKmd = await this.kcl.signTransaction( + this.handle, + this.wallet_pswd, + fundingTxnArgs + ); + const fundingResponse = await this.v2Client + .sendRawTransaction(stxKmd) + .do(); + const info = await algosdk.waitForConfirmation( + this.v2Client, + fundingResponse.txId, + 1 + ); + assert.ok(info['confirmed-round'] > 0); + } + ); + + Given( + 'I build an application transaction with the transient account, the current application, suggested params, operation {string}, approval-program {string}, clear-program {string}, global-bytes {int}, global-ints {int}, local-bytes {int}, local-ints {int}, app-args {string}, foreign-apps {string}, foreign-assets {string}, app-accounts {string}, extra-pages {int}, boxes {string}', + async function ( + operationString, + approvalProgramFile, + clearProgramFile, + numGlobalByteSlices, + numGlobalInts, + numLocalByteSlices, + numLocalInts, + appArgsCommaSeparatedString, + foreignAppsCommaSeparatedString, + foreignAssetsCommaSeparatedString, + appAccountsCommaSeparatedString, + extraPages, + boxesCommaSeparatedString + ) { + if (operationString === 'create') { + this.currentApplicationIndex = 0; + } + + // operation string to enum + const operation = operationStringToEnum(operationString); + // open and load in approval program + let approvalProgramBytes; + if (approvalProgramFile !== '') { + approvalProgramBytes = await compileProgram( + this.v2Client, + approvalProgramFile + ); + } + // open and load in clear program + let clearProgramBytes; + if (clearProgramFile !== '') { + clearProgramBytes = await compileProgram( + this.v2Client, + clearProgramFile + ); + } + // split and process app args + let appArgs; + if (appArgsCommaSeparatedString !== '') { + appArgs = splitAndProcessAppArgs(appArgsCommaSeparatedString); + } + // split and process foreign apps + let foreignApps; + if (foreignAppsCommaSeparatedString !== '') { + foreignApps = []; + foreignAppsCommaSeparatedString + .split(',') + .forEach((foreignAppAsString) => { + foreignApps.push(parseInt(foreignAppAsString)); + }); + } + // split and process foreign assets + let foreignAssets; + if (foreignAssetsCommaSeparatedString !== '') { + foreignAssets = []; + foreignAssetsCommaSeparatedString + .split(',') + .forEach((foreignAssetAsString) => { + foreignAssets.push(parseInt(foreignAssetAsString)); + }); + } + // split and process app accounts + let appAccounts; + if (appAccountsCommaSeparatedString !== '') { + appAccounts = appAccountsCommaSeparatedString.split(','); + } + // split and process box references + let boxes; + if (boxesCommaSeparatedString !== '') { + boxes = splitAndProcessBoxReferences(boxesCommaSeparatedString); + } + const sp = await this.v2Client.getTransactionParams().do(); + if (sp.firstRound === 0) sp.firstRound = 1; + const o = { + type: 'appl', + from: this.transientAccount.addr, + suggestedParams: sp, + appIndex: this.currentApplicationIndex, + appOnComplete: operation, + appLocalInts: numLocalInts, + appLocalByteSlices: numLocalByteSlices, + appGlobalInts: numGlobalInts, + appGlobalByteSlices: numGlobalByteSlices, + appApprovalProgram: approvalProgramBytes, + appClearProgram: clearProgramBytes, + appArgs, + appAccounts, + appForeignApps: foreignApps, + appForeignAssets: foreignAssets, + boxes, + extraPages, + }; + this.txn = new algosdk.Transaction(o); + } + ); + + Given( + 'I sign and submit the transaction, saving the txid. If there is an error it is {string}.', + async function (errorString) { + try { + const appStx = this.txn.signTxn(this.transientAccount.sk); + this.appTxid = await this.v2Client.sendRawTransaction(appStx).do(); + } catch (err) { + if (errorString !== '') { + // error was expected. check that err.message includes expected string. + const errorContainsString = err.message.includes(errorString); + assert.deepStrictEqual(true, errorContainsString); + } else { + // unexpected error, rethrow. + throw err; + } + } + } + ); + + Given('I wait for the transaction to be confirmed.', async function () { + const info = await algosdk.waitForConfirmation( + this.v2Client, + this.appTxid.txId, + 1 + ); + assert.ok(info['confirmed-round'] > 0); + }); + + Given('I reset the array of application IDs to remember.', async function () { + this.appIDs = []; + }); + + Given('I remember the new application ID.', async function () { + const info = await this.v2Client + .pendingTransactionInformation(this.appTxid.txId) + .do(); + this.currentApplicationIndex = info['application-index']; + + if (!Object.prototype.hasOwnProperty.call(this, 'appIDs')) { + this.appIDs = []; + } + this.appIDs.push(this.currentApplicationIndex); + }); + + Then( + 'The transient account should have the created app {string} and total schema byte-slices {int} and uints {int},' + + ' the application {string} state contains key {string} with value {string}', + async function ( + appCreatedBoolAsString, + numByteSlices, + numUints, + applicationState, + stateKey, + stateValue + ) { + const accountInfo = await this.v2Client + .accountInformation(this.transientAccount.addr) + .do(); + const appTotalSchema = accountInfo['apps-total-schema']; + assert.strictEqual(appTotalSchema['num-byte-slice'], numByteSlices); + assert.strictEqual(appTotalSchema['num-uint'], numUints); + + const appCreated = appCreatedBoolAsString === 'true'; + const createdApps = accountInfo['created-apps']; + // If we don't expect the app to exist, verify that it isn't there and exit. + if (!appCreated) { + for (let i = 0; i < createdApps.length; i++) { + assert.notStrictEqual( + createdApps[i].id, + this.currentApplicationIndex + ); + } + return; + } + + let foundApp = false; + for (let i = 0; i < createdApps.length; i++) { + foundApp = + foundApp || createdApps[i].id === this.currentApplicationIndex; + } + assert.ok(foundApp); + + // If there is no key to check, we're done. + if (stateKey === '') { + return; + } + + let foundValueForKey = false; + let keyValues = []; + if (applicationState === 'local') { + let counter = 0; + for (let i = 0; i < accountInfo['apps-local-state'].length; i++) { + const localState = accountInfo['apps-local-state'][i]; + if (localState.id === this.currentApplicationIndex) { + keyValues = localState['key-value']; + counter += 1; + } + } + assert.strictEqual(counter, 1); + } else if (applicationState === 'global') { + let counter = 0; + for (let i = 0; i < accountInfo['created-apps'].length; i++) { + const createdApp = accountInfo['created-apps'][i]; + if (createdApp.id === this.currentApplicationIndex) { + keyValues = createdApp.params['global-state']; + counter += 1; + } + } + assert.strictEqual(counter, 1); + } else { + assert.fail( + `test does not understand given application state: ${applicationState}` + ); + } + + assert.ok(keyValues.length > 0); + + for (let i = 0; i < keyValues.length; i++) { + const keyValue = keyValues[i]; + const foundKey = keyValue.key; + if (foundKey === stateKey) { + foundValueForKey = true; + const foundValue = keyValue.value; + if (foundValue.type === 1) { + assert.strictEqual(foundValue.bytes, stateValue); + } else if (foundValue.type === 0) { + assert.strictEqual(foundValue.uint, stateValue); + } + } + } + assert.ok(foundValueForKey); + } + ); + + Then('fee field is in txn', async function () { + const s = algosdk.decodeObj(this.stx); + const { txn } = s; + assert.strictEqual('fee' in txn, true); + }); + + Then('fee field not in txn', async function () { + const s = algosdk.decodeObj(this.stx); + const { txn } = s; + assert.strictEqual(!('fee' in txn), true); + }); + + When( + 'I create the Method object from method signature {string}', + function (signature) { + this.method = algosdk.ABIMethod.fromSignature(signature); + } + ); + + When( + 'I create the Method object with name {string} first argument type {string} second argument type {string} and return type {string}', + function (name, firstArgType, secondArgType, returnType) { + this.method = makeABIMethod( + makeObject({ + name, + args: makeArray( + makeObject({ + type: firstArgType, + }), + makeObject({ + type: secondArgType, + }) + ), + returns: makeObject({ type: returnType }), + }) + ); + } + ); + + When( + 'I create the Method object with name {string} first argument name {string} first argument type {string} second argument name {string} second argument type {string} and return type {string}', + function ( + name, + firstArgName, + firstArgType, + secondArgName, + secondArgType, + returnType + ) { + this.method = makeABIMethod( + makeObject({ + name, + args: makeArray( + makeObject({ name: firstArgName, type: firstArgType }), + makeObject({ name: secondArgName, type: secondArgType }) + ), + returns: makeObject({ type: returnType }), + }) + ); + } + ); + + When( + 'I create the Method object with name {string} method description {string} first argument type {string} first argument description {string} second argument type {string} second argument description {string} and return type {string}', + function ( + name, + methodDesc, + firstArgType, + firstArgDesc, + secondArgType, + secondArgDesc, + returnType + ) { + this.method = makeABIMethod( + makeObject({ + name, + desc: methodDesc, + args: makeArray( + makeObject({ type: firstArgType, desc: firstArgDesc }), + makeObject({ type: secondArgType, desc: secondArgDesc }) + ), + returns: makeObject({ type: returnType }), + }) + ); + } + ); + + When('I serialize the Method object into json', function () { + this.json = JSON.stringify(this.method); + }); + + Then( + 'the method selector should be {string}', + function (expectedSelectorHex) { + const actualSelector = this.method.getSelector(); + const expectedSelector = makeUint8Array( + Buffer.from(expectedSelectorHex, 'hex') + ); + assert.deepStrictEqual(actualSelector, expectedSelector); + } + ); + + Then('the txn count should be {int}', function (expectedCount) { + const actualCount = this.method.txnCount(); + assert.strictEqual(actualCount, parseInt(expectedCount)); + }); + + Then( + 'the deserialized json should equal the original Method object', + function () { + const deserializedMethod = makeABIMethod(parseJSON(this.json)); + assert.deepStrictEqual(deserializedMethod, this.method); + } + ); + + When( + 'I create an Interface object from the Method object with name {string} and description {string}', + function (name, desc) { + this.interface = new algosdk.ABIInterface( + makeObject({ + name, + desc, + methods: makeArray(this.method.toJSON()), + }) + ); + } + ); + + When('I serialize the Interface object into json', function () { + this.json = JSON.stringify(this.interface); + }); + + Then( + 'the deserialized json should equal the original Interface object', + function () { + const deserializedInterface = new algosdk.ABIInterface( + parseJSON(this.json) + ); + assert.deepStrictEqual(deserializedInterface, this.interface); + } + ); + + When( + 'I create a Contract object from the Method object with name {string} and description {string}', + function (name, desc) { + this.contract = makeABIContract( + makeObject({ + name, + desc, + methods: makeArray(this.method.toJSON()), + }) + ); + } + ); + + When( + "I set the Contract's appID to {int} for the network {string}", + function (appID, network) { + this.contract.networks[network] = makeObject({ + appID: parseInt(appID, 10), + }); + } + ); + + When('I serialize the Contract object into json', function () { + this.json = JSON.stringify(this.contract); + }); + + Then( + 'the deserialized json should equal the original Contract object', + function () { + const deserializedContract = makeABIContract(parseJSON(this.json)); + assert.deepStrictEqual(deserializedContract, this.contract); + } + ); + + Then( + 'the produced json should equal {string} loaded from {string}', + function (expectedJson) { + // compare parsed JSON to avoid differences between encoded field order + assert.deepStrictEqual(JSON.parse(this.json), JSON.parse(expectedJson)); + } + ); + + Given('a new AtomicTransactionComposer', function () { + this.composer = new algosdk.AtomicTransactionComposer(); + }); + + Given('an application id {int}', function (appId) { + this.currentApplicationIndex = parseInt(appId, 10); + }); + + When('I make a transaction signer for the signing account.', function () { + this.transactionSigner = algosdk.makeBasicAccountTransactionSigner( + this.signingAccount + ); + }); + + When('I make a transaction signer for the transient account.', function () { + this.transactionSigner = algosdk.makeBasicAccountTransactionSigner( + this.transientAccount + ); + }); + + When( + 'I create a transaction with signer with the current transaction.', + function () { + this.transactionWithSigner = { + txn: this.txn, + signer: this.transactionSigner, + }; + } + ); + + When( + 'I create a transaction with an empty signer with the current transaction.', + function () { + this.transactionWithSigner = { + txn: this.txn, + signer: algosdk.makeEmptyTransactionSigner(), + }; + } + ); + + When('I create a new method arguments array.', function () { + this.encodedMethodArguments = []; + }); + + When( + 'I append the encoded arguments {string} to the method arguments array.', + function (commaSeparatedB64Args) { + if (commaSeparatedB64Args.length === 0) { + return; + } + const rawArgs = commaSeparatedB64Args.split(','); + + // Optionally parse ctxAppIds + const args = []; + for (let i = 0; i < rawArgs.length; i++) { + let b64Arg = rawArgs[i]; + if (b64Arg.includes('ctxAppIdx')) { + // Retrieve the n'th app id in the saved array of app ids + b64Arg = b64Arg.split(':'); + const appID = this.appIDs[parseInt(b64Arg[1], 10)]; + args.push(algosdk.encodeUint64(appID)); + } else { + args.push(makeUint8Array(Buffer.from(b64Arg, 'base64'))); + } + } + this.encodedMethodArguments.push(...args); + } + ); + + When( + 'I append the current transaction with signer to the method arguments array.', + function () { + this.encodedMethodArguments.push(this.transactionWithSigner); + } + ); + + async function addMethodCallToComposer( + sender, + onComplete, + approvalProgramFile, + clearProgramFile, + globalBytes, + globalInts, + localBytes, + localInts, + extraPages, + note + ) { + // open and load in approval program + let approvalProgramBytes; + if (approvalProgramFile !== '') { + approvalProgramBytes = await compileProgram( + this.v2Client, + approvalProgramFile + ); + } + // open and load in clear program + let clearProgramBytes; + if (clearProgramFile !== '') { + clearProgramBytes = await compileProgram(this.v2Client, clearProgramFile); + } + + const methodArgs = []; + + assert.strictEqual( + this.encodedMethodArguments.length, + this.method.args.length + ); + + for (let i = 0; i < this.method.args.length; i++) { + const argSpec = this.method.args[i]; + const encodedArg = this.encodedMethodArguments[i]; + + if (algosdk.abiTypeIsTransaction(argSpec.type)) { + methodArgs.push(encodedArg); + continue; + } + + let typeToDecode = argSpec.type; + + if (algosdk.abiTypeIsReference(argSpec.type)) { + switch (argSpec.type) { + case algosdk.ABIReferenceType.account: + typeToDecode = algosdk.ABIType.from('address'); + break; + case algosdk.ABIReferenceType.application: + case algosdk.ABIReferenceType.asset: + typeToDecode = algosdk.ABIType.from('uint64'); + break; + default: + throw new Error(`Unknown reference type: ${argSpec.type}`); + } + } + + if (typeof typeToDecode === 'string') { + throw new Error(`Cannot decode with type: ${typeToDecode}`); + } + + methodArgs.push(typeToDecode.decode(encodedArg)); + } + + this.composer.addMethodCall({ + appID: this.currentApplicationIndex, + method: this.method, + methodArgs, + sender, + suggestedParams: this.suggestedParams, + onComplete: operationStringToEnum(onComplete), + approvalProgram: approvalProgramBytes, + clearProgram: clearProgramBytes, + numGlobalInts: globalInts, + numGlobalByteSlices: globalBytes, + numLocalInts: localInts, + numLocalByteSlices: localBytes, + extraPages, + note, + signer: this.transactionSigner, + }); + } + + When( + 'I add a method call with the transient account, the current application, suggested params, on complete {string}, current transaction signer, current method arguments.', + async function (onComplete) { + await addMethodCallToComposer.call( + this, + this.transientAccount.addr, + onComplete, + '', + '', + undefined, + undefined, + undefined, + undefined, + undefined + ); + } + ); + + When( + 'I add a method call with the signing account, the current application, suggested params, on complete {string}, current transaction signer, current method arguments.', + async function (onComplete) { + await addMethodCallToComposer.call( + this, + this.signingAccount.addr, + onComplete, + '', + '', + undefined, + undefined, + undefined, + undefined, + undefined + ); + } + ); + + When( + 'I add a method call with the transient account, the current application, suggested params, on complete {string}, current transaction signer, current method arguments, approval-program {string}, clear-program {string}, global-bytes {int}, global-ints {int}, local-bytes {int}, local-ints {int}, extra-pages {int}.', + async function ( + onComplete, + approvalProg, + clearProg, + globalBytes, + globalInts, + localBytes, + localInts, + extraPages + ) { + await addMethodCallToComposer.call( + this, + this.transientAccount.addr, + onComplete, + approvalProg, + clearProg, + parseInt(globalBytes, 10), + parseInt(globalInts, 10), + parseInt(localBytes, 10), + parseInt(localInts, 10), + parseInt(extraPages, 10) + ); + } + ); + + When( + 'I add a method call with the signing account, the current application, suggested params, on complete {string}, current transaction signer, current method arguments, approval-program {string}, clear-program {string}, global-bytes {int}, global-ints {int}, local-bytes {int}, local-ints {int}, extra-pages {int}.', + async function ( + onComplete, + approvalProg, + clearProg, + globalBytes, + globalInts, + localBytes, + localInts, + extraPages + ) { + await addMethodCallToComposer.call( + this, + this.signingAccount.addr, + onComplete, + approvalProg, + clearProg, + parseInt(globalBytes, 10), + parseInt(globalInts, 10), + parseInt(localBytes, 10), + parseInt(localInts, 10), + parseInt(extraPages, 10) + ); + } + ); + + When( + 'I add a method call with the transient account, the current application, suggested params, on complete {string}, current transaction signer, current method arguments, approval-program {string}, clear-program {string}.', + async function (onCompletion, approvalProg, clearProg) { + assert.strictEqual(onCompletion, 'update'); + await addMethodCallToComposer.call( + this, + this.transientAccount.addr, + onCompletion, + approvalProg, + clearProg, + undefined, + undefined, + undefined, + undefined, + undefined + ); + } + ); + + When( + 'I add a method call with the signing account, the current application, suggested params, on complete {string}, current transaction signer, current method arguments, approval-program {string}, clear-program {string}.', + async function (onCompletion, approvalProg, clearProg) { + assert.strictEqual(onCompletion, 'update'); + await addMethodCallToComposer.call( + this, + this.signingAccount.addr, + onCompletion, + approvalProg, + clearProg, + undefined, + undefined, + undefined, + undefined, + undefined + ); + } + ); + + Given('I add the nonce {string}', function (nonce) { + this.nonce = nonce; + }); + + Given( + 'I add a nonced method call with the transient account, the current application, suggested params, on complete {string}, current transaction signer, current method arguments.', + async function (onComplete) { + const nonce = makeUint8Array(Buffer.from(this.nonce)); + await addMethodCallToComposer.call( + this, + this.transientAccount.addr, + onComplete, + '', + '', + undefined, + undefined, + undefined, + undefined, + undefined, + nonce + ); + } + ); + + When( + 'I add the current transaction with signer to the composer.', + function () { + this.composer.addTransaction(this.transactionWithSigner); + } + ); + + When( + 'I build the transaction group with the composer. If there is an error it is {string}.', + function (errorType) { + if (errorType === '') { + // no error expected + this.composerBuiltGroup = this.composer.buildGroup(); + return; + } + + let expectedMessage; + switch (errorType) { + case 'zero group size error': + expectedMessage = 'Cannot build a group with 0 transactions'; + break; + default: + throw new Error(`Unknown error type: "${errorType}"`); + } + + assert.throws( + () => this.composer.buildGroup(), + (err) => err.message === expectedMessage + ); + } + ); + + Then('I clone the composer.', function () { + this.composer = this.composer.clone(); + }); + + Then( + 'The composer should have a status of {string}.', + function (expectedStatus) { + function statusStringToEnum(inString) { + switch (inString) { + case 'BUILDING': + return algosdk.AtomicTransactionComposerStatus.BUILDING; + case 'BUILT': + return algosdk.AtomicTransactionComposerStatus.BUILT; + case 'SIGNED': + return algosdk.AtomicTransactionComposerStatus.SIGNED; + case 'SUBMITTED': + return algosdk.AtomicTransactionComposerStatus.SUBMITTED; + case 'COMMITTED': + return algosdk.AtomicTransactionComposerStatus.COMMITTED; + default: + throw Error( + `did not recognize AtomicTransactionComposer status string ${inString}` + ); + } + } + + assert.strictEqual( + this.composer.getStatus(), + statusStringToEnum(expectedStatus) + ); + } + ); + + Then('I gather signatures with the composer.', async function () { + this.composerSignedTransactions = await this.composer.gatherSignatures(); + }); + + Then( + 'the base64 encoded signed transactions should equal {string}', + function (commaSeparatedB64SignedTxns) { + const expectedSignedTxns = commaSeparatedB64SignedTxns + .split(',') + .map((b64SignedTxn) => Buffer.from(b64SignedTxn, 'base64')); + + const actualSignedTxns = this.composerSignedTransactions.map( + (signedTxn) => Buffer.from(signedTxn) + ); + assert.deepStrictEqual( + [...actualSignedTxns], + [...expectedSignedTxns], + `Got ${actualSignedTxns + .map((stxn) => stxn.toString('base64')) + .join(',')}` + ); + } + ); + + Then( + 'I execute the current transaction group with the composer.', + async function () { + this.composerExecuteResponse = await this.composer.execute( + this.v2Client, + 4 + ); + assert.ok(this.composerExecuteResponse.confirmedRound > 0); + } + ); + + Then( + 'The app should have returned {string}.', + function (expectedReturnValues) { + const b64ExpectedReturnValues = expectedReturnValues.split(','); + + const { methodResults } = this.composerExecuteResponse; + assert.strictEqual(methodResults.length, b64ExpectedReturnValues.length); + + for (let i = 0; i < methodResults.length; i++) { + const actualResult = methodResults[i]; + const { method } = actualResult; + const expectedReturnValue = Buffer.from( + b64ExpectedReturnValues[i], + 'base64' + ); + + if (actualResult.decodeError) { + throw actualResult.decodeError; + } + assert.deepStrictEqual( + Buffer.from(actualResult.rawReturnValue), + expectedReturnValue, + `Actual return value for method at index ${i} does not match expected. Actual: ${Buffer.from( + actualResult.rawReturnValue + ).toString('base64')}` + ); + + const returnType = method.returns.type; + if (returnType === 'void') { + assert.strictEqual(expectedReturnValue.byteLength, 0); + continue; + } + + assert.deepStrictEqual( + actualResult.returnValue, + returnType.decode(expectedReturnValue) + ); + } + } + ); + + Then( + 'The app should have returned ABI types {string}.', + function (expectedAbiTypesString) { + // Each return from a unique ABI method call is separated by a colon + const expectedAbiTypes = expectedAbiTypesString.split(':'); + + const { methodResults } = this.composerExecuteResponse; + assert.strictEqual(methodResults.length, expectedAbiTypes.length); + + for (let i = 0; i < methodResults.length; i++) { + const expectedAbiType = expectedAbiTypes[i]; + const actualResult = methodResults[i]; + const { method } = actualResult; + + if (actualResult.decodeError) { + throw actualResult.decodeError; + } + + const returnType = method.returns.type; + if (returnType === 'void') { + assert.strictEqual(expectedAbiType, returnType); + continue; + } + + const expectedType = algosdk.ABIType.from(expectedAbiType); + const decodedResult = expectedType.decode(actualResult.rawReturnValue); + const roundTripResult = expectedType.encode(decodedResult); + + assert.deepStrictEqual(roundTripResult, actualResult.rawReturnValue); + } + } + ); + + Then( + 'The {int}th atomic result for randomInt\\({int}) proves correct', + function (resultIndex, methodArg) { + // Return format for randomInt method + const methodReturnType = algosdk.ABIType.from('(uint64,byte[17])'); + const actualResult = this.composerExecuteResponse.methodResults[ + resultIndex + ]; + const resultArray = methodReturnType.decode(actualResult.rawReturnValue); + assert.strictEqual(resultArray.length, 2); + const [randomIntResult, witnessResult] = resultArray; + + // Check the random int against the witness + const witnessHash = genericHash(witnessResult).slice(0, 8); + const witness = algosdk.bytesToBigInt(witnessHash); + const quotient = witness % BigInt(methodArg); + assert.strictEqual(quotient, randomIntResult); + } + ); + + Then( + 'The {int}th atomic result for randElement\\({string}) proves correct', + function (resultIndex, methodArg) { + // Return format for randElement method + const methodReturnType = algosdk.ABIType.from('(byte,byte[17])'); + const actualResult = this.composerExecuteResponse.methodResults[ + resultIndex + ]; + const resultArray = methodReturnType.decode(actualResult.rawReturnValue); + assert.strictEqual(resultArray.length, 2); + const [randomResult, witnessResult] = resultArray; + + // Check the random character against the witness + const witnessHash = genericHash(witnessResult).slice(0, 8); + const witness = algosdk.bytesToBigInt(witnessHash); + const quotient = witness % BigInt(methodArg.length); + assert.strictEqual( + methodArg[quotient], + Buffer.from(makeUint8Array([randomResult])).toString('utf-8') + ); + } + ); + + // Helper function to parse paths with '.' delimiters + function glom(result, pathString) { + const keys = pathString.split('.'); + let item = result; + for (let i = 0; i < keys.length; i++) { + let index = parseInt(keys[i], 10); + if (Number.isNaN(index)) { + index = keys[i]; + } + item = item[index]; + } + return item; + } + + Then( + 'I can dig the {int}th atomic result with path {string} and see the value {string}', + function (index, pathString, expectedResult) { + let actualResult = this.composerExecuteResponse.methodResults[index] + .txInfo; + actualResult = glom(actualResult, pathString); + + assert.strictEqual(expectedResult, actualResult.toString()); + } + ); + + Then( + 'I dig into the paths {string} of the resulting atomic transaction tree I see group ids and they are all the same', + function (pathString) { + const paths = pathString.split(':').map((p) => p.split(',')); + let groupID; + + for (let i = 0; i < paths.length; i++) { + const pathItem = paths[i]; + let actualResults = this.composerExecuteResponse.methodResults; + for (let j = 0; j < pathItem.length; j++) { + const itxnIndex = pathItem[j]; + if (j === 0) { + actualResults = actualResults[itxnIndex].txInfo; + } else { + actualResults = actualResults['inner-txns'][itxnIndex]; + } + + const thisGroupID = actualResults.txn.txn.group; + if (j === 0) { + groupID = thisGroupID; + } else { + assert.strictEqual(groupID, thisGroupID); + } + } + } + } + ); + + Then( + 'The {int}th atomic result for {string} satisfies the regex {string}', + function (index, method, regexString) { + // Only allow the "spin()" method + assert.strictEqual(method, 'spin()'); + + const abiType = algosdk.ABIType.from( + '(byte[3],byte[17],byte[17],byte[17])' + ); + const actualResult = this.composerExecuteResponse.methodResults[index]; + let spin = abiType.decode(actualResult.rawReturnValue)[0]; + spin = Buffer.from(spin).toString('utf-8'); + + assert.ok(spin.match(regexString)); + } + ); + + Given( + 'a dryrun response file {string} and a transaction at index {string}', + async function (drrFile, txId) { + const drContents = await loadResource(drrFile); + const js = parseJSON(drContents); + const drr = new algosdk.DryrunResult(js); + this.txtrace = drr.txns[parseInt(txId)]; + } + ); + + Then('calling app trace produces {string}', async function (expected) { + const traceString = this.txtrace.appTrace(); + const expectedString = (await loadResource(expected)).toString(); + assert.equal(traceString, expectedString); + }); + + When( + 'I append to my Method objects list in the case of a non-empty signature {string}', + function (methodsig) { + if (this.methods === undefined) this.methods = []; + if (methodsig !== '') + this.methods.push(algosdk.ABIMethod.fromSignature(methodsig)); + } + ); + + When('I create an Interface object from my Method objects list', function () { + this.iface = new algosdk.ABIInterface({ + name: '', + methods: this.methods.map((m) => m.toJSON()), + }); + }); + + When('I create a Contract object from my Method objects list', function () { + this.contract = new algosdk.ABIContract({ + name: '', + methods: this.methods.map((m) => m.toJSON()), + }); + }); + + When('I get the method from the Interface by name {string}', function (name) { + this.errorString = undefined; + this.retreived_method = undefined; + try { + this.retreived_method = this.iface.getMethodByName(name); + } catch (error) { + this.errorString = error.message; + } + this.methods = undefined; + }); + + When('I get the method from the Contract by name {string}', function (name) { + this.errorString = undefined; + this.retreived_method = undefined; + try { + this.retreived_method = this.contract.getMethodByName(name); + } catch (error) { + this.errorString = error.message; + } + this.methods = undefined; + }); + + Then( + 'the produced method signature should equal {string}. If there is an error it begins with {string}', + function (expectedSig, errString) { + if (this.retreived_method !== undefined) { + assert.strictEqual(true, errString === '' || errString === undefined); + assert.strictEqual(this.retreived_method.getSignature(), expectedSig); + } else if (this.errorString !== undefined) { + assert.strictEqual(true, this.retreived_method === undefined); + assert.strictEqual( + true, + this.errorString.includes(errString), + `expected ${errString} got ${this.errorString}` + ); + } else { + assert.ok(false, 'Both retrieved method and error are undefined'); + } + } + ); + + Then( + 'according to {string}, the contents of the box with name {string} in the current application should be {string}. If there is an error it is {string}.', + async function (fromClient, boxName, boxValue, errString) { + try { + const boxKey = splitAndProcessAppArgs(boxName)[0]; + + let resp = null; + if (fromClient === 'algod') { + resp = await this.v2Client + .getApplicationBoxByName(this.currentApplicationIndex, boxKey) + .do(); + } else if (fromClient === 'indexer') { + resp = await this.indexerV2client + .lookupApplicationBoxByIDandName( + this.currentApplicationIndex, + boxKey + ) + .do(); + } else { + assert.fail(`expecting algod or indexer, got ${fromClient}`); + } + + const actualName = resp.name; + const actualValue = resp.value; + assert.deepStrictEqual(Buffer.from(boxKey), Buffer.from(actualName)); + assert.deepStrictEqual( + Buffer.from(boxValue, 'base64'), + Buffer.from(actualValue) + ); + } catch (err) { + if (errString !== '') { + assert.deepStrictEqual( + true, + err.message.includes(errString), + `expected ${errString} got ${err.message}` + ); + } else { + throw err; + } + } + } + ); + + function splitBoxNames(boxB64Names) { + if (boxB64Names == null || boxB64Names === '') { + return []; + } + const splitBoxB64Names = boxB64Names.split(':'); + const boxNames = []; + splitBoxB64Names.forEach((subArg) => { + boxNames.push(makeUint8Array(Buffer.from(subArg, 'base64'))); + }); + return boxNames; + } + + Then( + 'according to indexer, with {int} being the parameter that limits results, and {string} being the parameter that sets the next result, the current application should have the following boxes {string}.', + async function (limit, nextPage, boxNames) { + const boxes = splitBoxNames(boxNames); + const resp = await this.indexerV2client + .searchForApplicationBoxes(this.currentApplicationIndex) + .limit(limit) + .nextToken(nextPage) + .do(); + + assert.deepStrictEqual(boxes.length, resp.boxes.length); + const actualBoxes = new Set( + resp.boxes.map((b) => Buffer.from(b.name, 'base64')) + ); + const expectedBoxes = new Set(boxes.map(Buffer.from)); + assert.deepStrictEqual(expectedBoxes, actualBoxes); + } + ); + + Then( + 'according to {string}, with {int} being the parameter that limits results, the current application should have {int} boxes.', + async function (fromClient, limit, expectedBoxNum) { + let resp = null; + if (fromClient === 'algod') { + resp = await this.v2Client + .getApplicationBoxes(this.currentApplicationIndex) + .max(limit) + .do(); + } else if (fromClient === 'indexer') { + resp = await this.indexerV2client + .searchForApplicationBoxes(this.currentApplicationIndex) + .limit(limit) + .do(); + } else { + assert.fail(`expecting algod or indexer, got ${fromClient}`); + } + + assert.deepStrictEqual(expectedBoxNum, resp.boxes.length); + } + ); + + Then( + 'according to {string}, the current application should have the following boxes {string}.', + async function (fromClient, boxNames) { + const boxes = splitBoxNames(boxNames); + + let resp = null; + if (fromClient === 'algod') { + resp = await this.v2Client + .getApplicationBoxes(this.currentApplicationIndex) + .do(); + } else if (fromClient === 'indexer') { + resp = await this.indexerV2client + .searchForApplicationBoxes(this.currentApplicationIndex) + .do(); + } else { + assert.fail(`expecting algod or indexer, got ${fromClient}`); + } + + assert.deepStrictEqual(boxes.length, resp.boxes.length); + const actualBoxes = new Set( + resp.boxes.map((b) => Buffer.from(b.name, 'base64')) + ); + const expectedBoxes = new Set(boxes.map(Buffer.from)); + assert.deepStrictEqual(expectedBoxes, actualBoxes); + } + ); + + Then( + 'I sleep for {int} milliseconds for indexer to digest things down.', + async (milliseconds) => { + const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); + await sleep(milliseconds); + } + ); + + Given('a source map json file {string}', async function (srcmap) { + const js = parseJSON(await loadResource(srcmap)); + this.sourcemap = new algosdk.SourceMap(js); + }); + + Then( + 'the string composed of pc:line number equals {string}', + function (mapping) { + const buff = Object.entries(this.sourcemap.pcToLine).map( + ([pc, line]) => `${pc}:${line}` + ); + assert.equal(buff.join(';'), mapping); + } + ); + + Then( + 'getting the line associated with a pc {string} equals {string}', + function (pc, expectedLine) { + const actualLine = this.sourcemap.getLineForPc(parseInt(pc)); + assert.equal(actualLine, parseInt(expectedLine)); + } + ); + + Then( + 'getting the last pc associated with a line {string} equals {string}', + function (line, expectedPc) { + const actualPcs = this.sourcemap.getPcsForLine(parseInt(line)); + assert.equal(actualPcs.pop(), parseInt(expectedPc)); + } + ); + + When( + 'I compile a teal program {string} with mapping enabled', + async function (teal) { + const tealSrc = await loadResource(teal); + const compiledResponse = await this.v2Client + .compile(tealSrc) + .sourcemap(true) + .do(); + this.rawSourceMap = JSON.stringify(compiledResponse.sourcemap); + } + ); + + Then( + 'the resulting source map is the same as the json {string}', + async function (expectedJsonPath) { + const expected = await loadResource(expectedJsonPath); + assert.equal(this.rawSourceMap, expected.toString().trim()); + } + ); + + Then( + 'disassembly of {string} matches {string}', + async function (bytecodeFilename, sourceFilename) { + const bytecode = await loadResource(bytecodeFilename); + const resp = await this.v2Client.disassemble(bytecode).do(); + const expectedSource = await loadResource(sourceFilename); + + assert.deepStrictEqual( + resp.result.toString('UTF-8'), + expectedSource.toString('UTF-8') + ); + } + ); + + When( + 'we make a GetLightBlockHeaderProof call for round {int}', + async function (int) { + await this.v2Client.getLightBlockHeaderProof(int).do(); + } + ); + + When('we make a GetStateProof call for round {int}', async function (int) { + await this.v2Client.getStateProof(int).do(); + }); + + When( + 'we make a Lookup Block Hash call against round {int}', + async function (int) { + await this.v2Client.getBlockHash(int).do(); + } + ); + + Given( + 'a base64 encoded program bytes for heuristic sanity check {string}', + async function (programByteStr) { + this.seeminglyProgram = new Uint8Array( + Buffer.from(programByteStr, 'base64') + ); + } + ); + + When('I start heuristic sanity check over the bytes', async function () { + this.actualErrMsg = undefined; + try { + new algosdk.LogicSigAccount(this.seeminglyProgram); // eslint-disable-line + } catch (e) { + this.actualErrMsg = e.message; + } + }); + + Then( + 'if the heuristic sanity check throws an error, the error contains {string}', + async function (errMsg) { + if (errMsg !== '') assert.ok(this.actualErrMsg.includes(errMsg)); + else assert.strictEqual(this.actualErrMsg, undefined); + } + ); + + When( + 'I prepare the transaction without signatures for simulation', + function () { + // Transform transaction into a "EncodedSignedTransaction", but don't + // sign it so we can check that we can simulate unsigned txns. + this.stx = algosdk.encodeUnsignedSimulateTransaction(this.txn); + } + ); + + Then('I simulate the transaction', async function () { + this.simulateResponse = await this.v2Client + .simulateRawTransactions(this.stx) + .do(); + }); + + Then( + 'I simulate the current transaction group with the composer', + async function () { + // Alias the simulate response as execute response so it can be re-used + // in other steps that check the ABI method results. + this.composerExecuteResponse = await this.composer.simulate( + this.v2Client + ); + this.simulateResponse = this.composerExecuteResponse.simulateResponse; + this.methodResults = this.composerExecuteResponse.methodResults; + } + ); + + Then( + 'the simulation should succeed without any failure message', + async function () { + for (const txnGroup of this.simulateResponse.txnGroups) { + assert.deepStrictEqual(undefined, txnGroup.failedMessage); + } + } + ); + + Then( + 'the simulation should report missing signatures at group {string}, transactions {string}', + async function (txnGroupIndex, transactionPath) { + // Parse the path ("0,0") into a list of numbers ([0, 0]) + const stringPath = transactionPath.split(','); + const txnIndexes = stringPath.map((n) => parseInt(n, 10)); + const groupNum = parseInt(txnGroupIndex, 10); + + // Check for missing signature flag + for (const txnIndex of txnIndexes) { + assert.deepStrictEqual( + true, + this.simulateResponse.txnGroups[groupNum].txnResults[txnIndex] + .missingSignature + ); + } + } + ); + + Then( + 'the simulation should report a failure at group {string}, path {string} with message {string}', + async function (txnGroupIndex, failAt, errorMsg) { + // Parse transaction group number + const groupNum = parseInt(txnGroupIndex, 10); + + // Parse the path ("0,0") into a list of numbers ([0, 0]) + const stringPath = failAt.split(','); + const failPath = stringPath.map((n) => parseInt(n, 10)); + + const failedMessage = this.simulateResponse.txnGroups[groupNum] + .failureMessage; + assert.ok( + failedMessage.includes(errorMsg), + `Error message: "${failedMessage}" does not contain "${errorMsg}"` + ); + + // Check path array + const { failedAt } = this.simulateResponse.txnGroups[groupNum]; + assert.deepStrictEqual(makeArray(...failedAt), makeArray(...failPath)); + } + ); + + When('I make a new simulate request.', async function () { + this.simulateRequest = new algosdk.modelsv2.SimulateRequest({ + txnGroups: [], + }); + }); + + Then('I allow more logs on that simulate request.', async function () { + this.simulateRequest.allowMoreLogging = true; + }); + + Then( + 'I simulate the transaction group with the simulate request.', + async function () { + this.composerExecuteResponse = await this.composer.simulate( + this.v2Client, + this.simulateRequest + ); + this.simulateResponse = this.composerExecuteResponse.simulateResponse; + this.methodResults = this.composerExecuteResponse.methodResults; + } + ); + + Then( + 'I check the simulation result has power packs allow-more-logging.', + async function () { + assert.ok(this.simulateResponse.evalOverrides); + assert.ok(this.simulateResponse.evalOverrides.maxLogCalls); + assert.ok(this.simulateResponse.evalOverrides.maxLogSize); + } + ); + + Then( + 'I allow {int} more budget on that simulate request.', + async function (budget) { + this.simulateRequest.extraOpcodeBudget = budget; + } + ); + + Then( + 'I check the simulation result has power packs extra-opcode-budget with extra budget {int}.', + async function (budget) { + assert.ok(this.simulateResponse.evalOverrides); + assert.strictEqual( + budget, + this.simulateResponse.evalOverrides.extraOpcodeBudget + ); + } + ); + + When('we make a Ready call', async function () { + await this.v2Client.ready().do(); + }); + + When( + 'we make a SetBlockTimeStampOffset call against offset {int}', + async function (offset) { + await this.v2Client.setBlockOffsetTimestamp(offset).do(); + } + ); + + When('we make a GetBlockTimeStampOffset call', async function () { + await this.v2Client.getBlockOffsetTimestamp().doRaw(); + }); + + When( + 'we make a SetSyncRound call against round {int}', + async function (round) { + await this.v2Client.setSyncRound(round).do(); + } + ); + + When('we make a GetSyncRound call', async function () { + await this.v2Client.getSyncRound().doRaw(); + }); + + When('we make a UnsetSyncRound call', async function () { + await this.v2Client.unsetSyncRound().do(); + }); + + When('we make an arbitrary algod call', async function () { + await this.v2Client.healthCheck().do(); + }); + + When('we make an arbitrary indexer call', async function () { + await this.indexerV2client.makeHealthCheck().do(); + }); + + When( + 'we make a TransactionGroupLedgerStateDeltaForRoundResponse call for round {int}', + async function (round) { + await this.v2Client + .getTransactionGroupLedgerStateDeltasForRound(round) + .doRaw(); + } + ); + + When( + 'we make a LedgerStateDeltaForTransactionGroupResponse call for ID {string}', + async function (id) { + await this.v2Client.getLedgerStateDeltaForTransactionGroup(id).do(); + } + ); + + When( + 'we make a GetLedgerStateDelta call against round {int}', + async function (round) { + await this.v2Client.getLedgerStateDelta(round).do(); + } + ); + + if (!options.ignoreReturn) { + return steps; + } + + return undefined; +}; diff --git a/tests/cucumber/unit.tags b/tests/cucumber/unit.tags new file mode 100644 index 0000000..2526360 --- /dev/null +++ b/tests/cucumber/unit.tags @@ -0,0 +1,38 @@ +@unit.abijson +@unit.abijson.byname +@unit.algod +@unit.algod.ledger_refactoring +@unit.applications +@unit.applications.boxes +@unit.atomic_transaction_composer +@unit.blocksummary +@unit.client-no-headers +@unit.dryrun +@unit.dryrun.trace.application +@unit.feetest +@unit.indexer +@unit.indexer.ledger_refactoring +@unit.indexer.logs +@unit.offline +@unit.program_sanity_check +@unit.ready +@unit.rekey +@unit.responses +@unit.responses.231 +@unit.responses.participationupdates +@unit.responses.unlimited_assets +@unit.responses.blocksummary +@unit.responses.statedelta.json +@unit.responses.sync +@unit.responses.timestamp +@unit.responses.txngroupdeltas.json +@unit.sourcemap +@unit.stateproof.paths +@unit.stateproof.responses +@unit.stateproof.responses.msgp +@unit.sync +@unit.tealsign +@unit.timestamp +@unit.transactions +@unit.transactions.keyreg +@unit.transactions.payment diff --git a/tests/mocha.js b/tests/mocha.js new file mode 100644 index 0000000..d2202ea --- /dev/null +++ b/tests/mocha.js @@ -0,0 +1,136 @@ +/* eslint-env node, mocha */ +/* eslint-disable no-console */ +const Mocha = require('mocha'); +const webpack = require('webpack'); +const fs = require('fs'); +const path = require('path'); + +const webpackConfig = require('../webpack.config'); + +const browser = process.env.TEST_BROWSER; + +async function testRunner() { + console.log('TEST_BROWSER is', browser); + + const testFiles = fs + .readdirSync(__dirname) + .filter( + (file) => + file !== 'mocha.js' && (file.endsWith('.js') || file.endsWith('.ts')) + ) + .map((file) => path.join(__dirname, file)); + + if (browser) { + const browserEntry = path.join(__dirname, 'browser', 'index.html'); + const bundleLocation = path.join(__dirname, 'browser', 'bundle.js'); + + await new Promise((resolve, reject) => { + // Change entry and output for webpack config + const webpackTestConfig = Object.assign(webpackConfig); + + webpackTestConfig.entry = testFiles; + webpackTestConfig.output = { + filename: path.basename(bundleLocation), + path: path.dirname(bundleLocation), + }; + + webpack(webpackTestConfig, (err, stats) => { + if (err || stats.hasErrors()) { + return reject(err || stats.toJson()); + } + return resolve(); + }); + }); + + console.log('Testing in browser'); + + /* eslint-disable global-require */ + if (browser === 'chrome') { + require('chromedriver'); + } else if (browser === 'firefox') { + require('geckodriver'); + } + /* eslint-disable global-require */ + + const webdriver = require('selenium-webdriver'); + const chrome = require('selenium-webdriver/chrome'); + const firefox = require('selenium-webdriver/firefox'); + + let chromeOptions = new chrome.Options(); + let firefoxOptions = new firefox.Options(); + + if (process.env.CI) { + chromeOptions = chromeOptions.addArguments( + 'no-sandbox', + 'headless', + 'disable-gpu' + ); + firefoxOptions = firefoxOptions.headless(); + } + + const driver = await new webdriver.Builder() + .setChromeOptions(chromeOptions) + .setFirefoxOptions(firefoxOptions) + .forBrowser(browser) + .build(); + + await driver.get(`file://${browserEntry}`); + + const title = await driver.getTitle(); + + if (title !== 'Algosdk Mocha Browser Testing') { + throw new Error(`Incorrect title: ${title}`); + } + + const { passed, failures } = await driver.executeAsyncScript( + async (done) => { + const failuresSeen = []; + let testsPassed = 0; + + const runner = mocha.run(() => { + done({ + passed: testsPassed, + failures: failuresSeen, + }); + }); + + runner.on('pass', () => { + testsPassed += 1; + }); + + runner.on('fail', (test, err) => { + failuresSeen.push({ + test: test.fullTitle(), + error: `${err.toString()}\n${err.stack}`, + }); + }); + } + ); + + console.log( + `Failed ${failures.length} of ${failures.length + passed} tests` + ); + + for (const { test, error } of failures) { + console.log(`Test: ${test}\n\t${error}\n`); + } + + await driver.quit(); + + process.exitCode = failures.length > 0 ? 1 : 0; + } else { + console.log('Testing in Node'); + + const mocha = new Mocha(); + testFiles.forEach((file) => mocha.addFile(file)); + + mocha.run((failures) => { + process.exitCode = failures ? 1 : 0; + }); + } +} + +testRunner().catch((err) => { + console.error(err); + process.exitCode = 1; +}); diff --git a/tsconfig-browser.json b/tsconfig-browser.json new file mode 100644 index 0000000..480fd2f --- /dev/null +++ b/tsconfig-browser.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/browser" + } +} diff --git a/tsconfig-cjs.json b/tsconfig-cjs.json new file mode 100644 index 0000000..a4c9f50 --- /dev/null +++ b/tsconfig-cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/cjs", + "module": "CommonJS" + } +} diff --git a/tsconfig-esm.json b/tsconfig-esm.json new file mode 100644 index 0000000..dc32abd --- /dev/null +++ b/tsconfig-esm.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/esm", + "declaration": true, + "declarationDir": "./dist/types" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0e86b21 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "lib": ["es5", "es2015.promise", "dom", "es2015"], + "outDir": "./dist", + "allowJs": true, + "target": "es2020", + "moduleResolution": "node", + "resolveJsonModule": true, + "esModuleInterop": true, + "sourceMap": true + }, + "include": ["./src"] +} diff --git a/typedoc.config.json b/typedoc.config.json new file mode 100644 index 0000000..ce478d0 --- /dev/null +++ b/typedoc.config.json @@ -0,0 +1,6 @@ +{ + "excludeExternals": true, + "entryPoints": ["./src/main.ts"], + "entryPointStrategy": "expand", + "out": "docs" +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..74f8bc7 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,36 @@ +const path = require('path'); + +module.exports = { + mode: 'production', + entry: './src/index.ts', + output: { + filename: 'algosdk.min.js', + path: path.resolve(__dirname, 'dist/browser'), + library: { + type: 'umd', + name: 'algosdk', + }, + }, + devtool: 'source-map', + resolve: { + // Add '.ts' as resolvable extensions + extensions: ['.ts', '.js'], + }, + module: { + rules: [ + // All files with a '.ts' extension will be handled by 'ts-loader'. + { + test: /\.ts$/, + loader: 'ts-loader', + options: { + configFile: path.resolve(__dirname, 'tsconfig-browser.json'), + }, + }, + + // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'. + { test: /\.js$/, loader: 'source-map-loader' }, + ], + // Don't parse tweetnacl module — https://github.com/dchest/tweetnacl-js/wiki/Using-with-Webpack + noParse: [/[\\/]tweetnacl[\\/]/, /[\\/]tweetnacl-auth[\\/]/], + }, +};